给出一个基环树森林,求每一棵基环树的最大直径
首先用dfs找到树上的环,然后去计算环上的/环外的路径最大长度即可
也就是一个树形dp的过程,需要一个队列优化
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int n,tot,l,que[maxn],tim,id[maxn],vis[maxn],fa[maxn],mark[maxn],times,inin[maxn],dep[maxn],head[maxn];
long long b[maxn],maxdp,dp[maxn],a[maxn];
vector <int> cir[maxn];
struct edge
{int to,nxt,v;
}e[maxn<<1];
void add(int x,int y,int z)
{e[++tot].nxt=head[x];e[tot].to=y;head[x]=tot;e[tot].v=z;inin[y]++;
}
void dfs(int x,int deep,int laste,int from)
{fa[x]=from; dep[x]=deep;for(int i=head[x];i;i=e[i].nxt){int to=e[i].to;if(!dep[to]) dfs(to,deep+1,i,x);else if((laste!=i-1 && laste!=i+1) && dep[to]<dep[x]){++times;for(int now=x;now!=to;now=fa[now])cir[times].push_back(now);cir[times].push_back(to);}}
}
void getw(int x,long long d,int laste)
{vis[x]++; id[++tim]=x; b[tim]=d;for(int i=head[x];i;i=e[i].nxt){int to=e[i].to;if(laste!=i-1&& laste!=i+1 && vis[to]<2 && mark[to])getw(to,d+e[i].v,i);}
}
void DP(int x,int fa)
{for(int i=head[x];i;i=e[i].nxt){int to=e[i].to;if(to==fa || mark[to] ) continue;DP(to,x);maxdp=max(maxdp,dp[x]+dp[to]+e[i].v);dp[x]=max(dp[x],dp[to]+e[i].v);}
}
int q[maxn];
long long solve(int x)
{long long res=0;for(int k=0;k<cir[x].size();k++) mark[cir[x][k]]=1;tim=0,getw(cir[x][0],0,0);for(int i=1;i<=cir[x].size();i++){int x=id[i]; maxdp=0,DP(x,0),res=max(res,maxdp);a[i]=dp[x];}int len=cir[x].size();for(int i=1;i<=cir[x].size();i++) a[i+len]=a[i];int l=1,r=1; q[l]=1;for (int i=2;i<=tim;i++){while (l<=r&&i-q[l]>=len)l++;int j=q[l]; if (l<=r)res=max(res,a[i]+a[j]+b[i]-b[j]);while (l<=r&&a[i]-b[i]>a[q[r]]-b[q[r]])r--;q[++r]=i;}return res;
}
int main()
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);scanf("%d",&n);int x,y;for(int i=1;i<=n;i++){scanf("%d%d",&x,&y);add(i,x,y); add(x,i,y); } for(int i=1;i<=n;i++)if(!dep[i])dfs(i,1,0,0);long long ans=0;for(int i=1;i<=times;i++)ans+=solve(i);printf("%lld",ans);return 0;
}