题目大意:
找基环树直径
(这个题输入给出的是内向基环树(虽然是无向边))
存在两种情况:
1.直径在树上。
2.直径从树里走到环上,再走进另外一个树里。
首先dfs找到环。
第一种直接树形dp。dp[i]i往下最长路径。还能用来求第二种情况。
第二种,找到环之后,断环成链,复制一倍。求的是,选择距离小于环长的两个点,贡献是两个点的dp[i],和两个点之间的距离。这个用单调队列优化dp即可。
bzoj会爆栈mmp
如果你是ywy可以bfs求树形dp
其实,这是一个内向基环树,所以,直接topo排序,从叶子往上面topo
即可求得dp,还能求得环。
代码:(dfs爆栈版)
#pragma comment(linker, "/STACK:102400000,102400000") #include<bits/stdc++.h> #define il inline #define reg register int #define numb (ch^‘0‘) using namespace std; typedef long long ll; void rd(int &x){ char ch;x=0; while(!isdigit(ch=getchar())); for(x=numb;isdigit(ch=getchar());x=x*10+numb); } namespace Miracle{ const int N=1e6+5; int n; struct node{ int nxt,to; ll val; }e[2*N]; int hd[N],cnt=1; void add(int x,int y,int z){ e[++cnt].nxt=hd[x]; e[cnt].to=y; e[cnt].val=z; hd[x]=cnt; } bool vis[N]; bool on[N];//on the huan bool in[N]; int to[N],v[N]; int sta[N],top; int len;//huan chang int mem[2*N],tot; ll cos[2*N]; int q[2*N],l,r; ll f[N]; ll ans; ll sum; ll zhi; bool fl; void dfs(int x,int in_edge){ // cout<<" dfs "<<x<<" "<<fl<<" "<<top<<endl; vis[x]=1; sta[++top]=x; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(i==(in_edge^1)) continue; if(!vis[y]){ dfs(y,i); } else if(!fl){ fl=true; // cout<<" find "<<top<<" : "<<sta[top]<<endl; int z; do{ z=sta[top]; on[z]=1; mem[++len]=z; top--; }while(z!=y); } } if(sta[top]==x) --top;//warning!!! } void dp(int x,int fa){//find farthest for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(y==fa) continue; if(on[y]) continue; dp(y,x); zhi=max(zhi,f[x]+e[i].val+f[y]); f[x]=max(f[x],e[i].val+f[y]); } } ll wrk(int st){ sum=0; len=0; fl=false;top=0; dfs(st,0); for(reg i=1;i<=len;++i){ zhi=0; dp(mem[i],0); sum=max(sum,zhi); mem[i+len]=mem[i]; } l=1,r=0; cos[1]=0; for(reg i=2;i<=2*len;++i){ cos[i]=(to[mem[i]]==mem[i-1])?v[mem[i]]:v[mem[i-1]]; cos[i]+=cos[i-1]; } for(reg i=1;i<=2*len;++i){ while(l<=r&&i-q[l]>=len) ++l; if(i!=1) sum=max(sum,f[mem[q[l]]]-cos[q[l]]+cos[i]+f[mem[i]]); while(l<=r&&f[mem[q[r]]]-cos[q[r]]<=f[mem[i]]-cos[i]) --r; q[++r]=i; } return sum; } int main(){ rd(n);int x;int z; for(reg i=1;i<=n;++i){ rd(x);rd(z); to[i]=x; v[i]=z; add(i,x,z);add(x,i,z); } for(reg i=1;i<=n;++i){ if(!vis[i]){ ans+=wrk(i); } } printf("%lld",ans); return 0; } } int main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2018/11/4 9:27:26 */
原文地址:https://www.cnblogs.com/Miracevin/p/9903327.html
时间: 2024-10-09 15:21:59