这道题就是求出来每一棵基环树的直径加起来。
对于每一棵基环树,先找见环。
直径可能在环上以某一点为根的子树里,也可能横跨两个子树中。
修改过程:
1.忘了考虑直径可能在以某一个点为根的子树里。
2.中间变量没有用long long
3.现在一直都是command terminated,就是在findcir中间就停不下来了。。
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cmath> #define LL long long #define M 1000005 using namespace std; LL ans=0; int all; struct edge { int y,ne; LL l; }e[3*M]; int p,h[M],v[M],Link[M],pos[M],root,now,n,ok[M],fa[M],c[M],cir[M],L,R,tot,num; LL dep[M],depest,d[M],pre[M],q[M],l[M]; void Addedge(int x,int y,LL l) { tot++; e[tot].y=y; e[tot].ne=h[x]; e[tot].l=l; h[x]=tot; } void Clear_v(int x) { v[x]=0; for (int i=h[x];i;i=e[i].ne) { int y=e[i].y; if (v[y]) Clear_v(y); } } void Findcir(int x) { v[x]=++now; for (int i=h[x];i;i=e[i].ne) { int y=e[i].y; if (!v[y]) { fa[y]=x; Findcir(y); } else if (y!=fa[x]&&v[y]<v[x]) { cir[++num]=y; c[y]=1; while (x!=y) c[x]=1,cir[++num]=x,x=fa[x]; return; } } } void Dfs(int x,LL dep) { v[x]=1; for (int i=h[x];i;i=e[i].ne) { int y=e[i].y; if (!v[y]&&(!c[y]||y==root)) { if (dep+e[i].l>depest) depest=dep+e[i].l,p=y; Dfs(y,dep+e[i].l); } } } void Getdep(int x,LL dep) { v[x]=1; for (int i=h[x];i;i=e[i].ne) { int y=e[i].y; if (v[y]||c[y]) continue; if (depest<dep+e[i].l) depest=dep+e[i].l; Getdep(y,dep+e[i].l); } } LL Getdis(int x,int y) { if (ok[x]&&Link[x]==y) return l[x]; return l[y]; } void Dp() { depest=0LL; L=1,R=0; q[++R]=dep[2]+Getdis(cir[1],cir[2]); pos[R]=2; pre[2]=Getdis(cir[1],cir[2]); for (int i=3;i<=num;i++) { pre[i]=pre[i-1]+Getdis(cir[i-1],cir[i]); while (R&&pre[i]+dep[i]>=q[R]) R--; q[++R]=pre[i]+dep[i]; pos[R]=i; } depest=q[L]+dep[1]; for (int i=2;i<=num;i++) { int add=i+num-1; pre[add]=pre[add-1]+Getdis(cir[add-1],cir[add]); if (pos[L]==i) L++; LL neww=pre[add]+dep[add]; while (R>=L&&neww>=q[R]) R--; q[++R]=neww; pos[R]=add; if (q[L]+dep[i]-pre[i]>depest) depest=q[L]+dep[i]-pre[i]; } } void Solve(int x) { now=0; num=0; Findcir(x); Clear_v(x); ans=0LL; if (num) { for (int i=1;i<=num;i++) { depest=0LL; root=cir[i]; p=cir[i]; Dfs(p,0LL); Clear_v(p); depest=0LL; Dfs(p,0LL); if (depest>ans) ans=depest; } Clear_v(x); for (int i=1;i<=num;i++) { depest=0LL; Getdep(cir[i],0LL); dep[i]=depest; } for (int i=1;i<=num;i++) dep[i+num]=dep[i],cir[i+num]=cir[i]; for (int i=1;i<=num;i++) c[cir[i]]=0; Dp(); if (depest<ans) depest=ans; } else { p=x; root=0; depest=0LL; Dfs(x,0LL); Clear_v(x); depest=0LL; Dfs(p,0LL); } } int main() { freopen("isl.in","r",stdin);freopen("isl.out","w",stdout); scanf("%d",&n); for (int i=1;i<=n;i++) { ok[i]=1; v[i]=c[i]=0; scanf("%d%lld",&Link[i],&l[i]); } for (int i=1;i<=n;i++) if (ok[i]&&Link[Link[i]]==i) { if (l[i]<l[Link[i]]) ok[i]=0; else ok[Link[i]]=0; } for (int i=1;i<=n;i++) if (ok[i]) Addedge(i,Link[i],l[i]),Addedge(Link[i],i,l[i]); LL ans=0LL; for (int i=1;i<=n;i++) if (!v[i]) Solve(i),ans+=depest,cout<<depest<<endl; printf("%lld\n",ans); return 0; }
最终还是没有找到我的算法中哪有问题,于是看了lyd的代码,非常短也很好理解。
首先找到所有的连通分量。
然后对于每一个连通分量,通过拓扑排序直接把子树处理完,并且找到环的(环上的点的度数永远不可能变成0),同时也处理了直径可能不在环上的情况。
方法是:
对于一个点y,f[y]表示以y为根的子树的最长长度,用儿子x来更新y。
在更新y之前先用f[x]+f[y]+1更新答案(不包含环的树的直径),因为此时f[y]没有被f[x]更新,他一定是通过别的儿子更
新的,然后再用f[x]更新y。
这样就处理完了环上的每个点子树的最长长度,接下来拆环为链,用单调队列来做即可。
#include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cstring> #include <queue> #define M 1000005 #define LL long long using namespace std; struct edge { int y,ne,v; }e[M*2]; int h[M],v[M],c[M],du[M],q[2*M],n,m,tot,t; LL f[M],d[M],a[2*M],b[2*M]; void Addedge(int x,int y,int v) { tot++; e[tot].y=y; e[tot].ne=h[x]; h[x]=tot; e[tot].v=v; du[x]++; } void dfs(int x,int k) { v[x]=1,c[x]=k; for (int i=h[x];i;i=e[i].ne) { int y=e[i].y; if (v[y]) continue; dfs(y,k); } } void Topsort() { int l=1,r=0,y; for (int i=1;i<=n;i++) if (du[i]==1) q[++r]=i; while (l<=r) { int x=q[l]; for (int i=h[x];i;i=e[i].ne) if (du[y=e[i].y]>1) { du[y]--; d[c[x]]=max(d[c[x]],f[x]+f[y]+e[i].v); f[y]=max(f[y],f[x]+e[i].v); if (du[y]==1) q[++r]=y; } l++; } } void Dp(int t,int x) { int m=0,i,y=x; do { a[++m]=f[y],du[y]=1; for (i=h[y];i;i=e[i].ne) if (du[e[i].y]>1) { y=e[i].y; b[m+1]=b[m]+e[i].v; break; } }while (i); if (m==2) { int l=0; for (int i=h[y];i;i=e[i].ne) if (e[i].y==x) l=max(l,e[i].v); d[t]=max(d[t],f[x]+f[y]+l); return; } for (int i=h[y];i;i=e[i].ne) if (e[i].y==x) { b[m+1]=b[m]+e[i].v; break; } for (int i=1;i<=m;i++) { a[m+i]=a[i]; b[m+i]=b[m+1]+b[i]; } int l,r; q[l=r=1]=1; for (int i=2;i<2*m;i++) { while (l<=r&&i-q[l]>=m) l++; d[t]=max(d[t],a[i]+a[q[l]]+b[i]-b[q[l]]); while (l<=r&&a[q[r]]+b[i]-b[q[r]]<=a[i]) r--; q[++r]=i; } } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) { int x,y; scanf("%d%d",&x,&y); Addedge(x,i,y); Addedge(i,x,y); } memset(v,0,sizeof(v)); t=0; for (int i=1;i<=n;i++) if (!c[i]) dfs(i,++t); Topsort(); LL ans=0LL; memset(v,0,sizeof(v)); for (int i=1;i<=n;i++) if (du[i]>1&&!v[c[i]]) { v[c[i]]=1; Dp(c[i],i); ans+=d[c[i]]; } cout<<ans<<endl; return 0; }
感悟:
1.拓扑序来找环的方法很巧妙
2.拆环为链,用单调队列优化dp
3.lyd博客