给定一个基环树森林,求每一棵基环树的直径,他们的和就是答案。
基环树的直径:先找到环,然后直径只有可能是
1.环上某一个点的子树的直径
2.环上某两个点之间的距离加上他们各自子树中最深的点的深度。
然后搜一搜就好了,情况2可以单调队列一下。
O
(
n
)
O(n)
O(n)
我写的太丑了,bzoj跑不过去qaq
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 1000010
int n,h[N],num=1,fa[N],dfn[N],dfnum=0,q[N],qh,qt;
ll mx1[N],mx2[N],ans=0,d[N],dis[N],res=0;
bool f[N];
struct edge{
int to,nxt,val;
}data[N<<1];
vector<int>cirid;
void findcir(int x,int ii){
dfn[x]=++dfnum;
for(int i=h[x];i;i=data[i].nxt){
if(!cirid.empty()) return;
if(i==(ii^1)) continue;
int y=data[i].to;
if(dfn[y]>dfn[x]) continue;
d[y]=data[i].val;
if(!dfn[y]){
fa[y]=x;findcir(y,i);continue;
}
if(dfn[y]<dfn[x]){
int xx=x;
while(xx!=fa[y]) cirid.push_back(xx),xx=fa[xx];
}
}
}
void dfs1(int x,int ii){
for(int i=h[x];i;i=data[i].nxt){
if(i==(ii^1)) continue;
int y=data[i].to;
if(f[y]) continue;
dfs1(y,i);
ll w=mx1[y]+data[i].val;
if(w>mx1[x]) mx2[x]=mx1[x],mx1[x]=w;
else if(w>mx2[x]) mx2[x]=w;
}
ans=max(ans,mx1[x]+mx2[x]);
}
int main(){
// freopen("a.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;++i){
int x,w;scanf("%d%d",&x,&w);
data[++num].to=i;data[num].nxt=h[x];h[x]=num;data[num].val=w;
data[++num].to=x;data[num].nxt=h[i];h[i]=num;data[num].val=w;
}
for(int x=1;x<=n;++x){
if(dfn[x]) continue;
cirid.clear();ans=0;findcir(x,0);
int m=cirid.size();
for(int i=0;i<m;++i) f[cirid[i]]=1;
for(int i=0;i<m;++i) dfs1(cirid[i],0);
for(int i=0;i<m;++i) cirid.push_back(cirid[i]);
qh=1,qt=0;
q[++qt]=0;dis[0]=0;
for(int i=1;i<cirid.size();++i){
dis[i]=dis[i-1]+d[cirid[i-1]];
while(qh<=qt&&q[qh]<i+1-m) ++qh;
ans=max(ans,mx1[cirid[i]]+dis[i]+mx1[cirid[q[qh]]]-dis[q[qh]]);
while(qh<=qt&&mx1[cirid[q[qt]]]-dis[q[qt]]<=mx1[cirid[i]]-dis[i]) --qt;
q[++qt]=i;
}
res+=ans;
}
printf("%lld\n",res);
return 0;
}