[SHOI2008]仙人掌图 II——树形dp与环形处理
题意:
给定一个仙人掌,边权为1
距离定义为两个点之间的最短路径
直径定义为距离最远的两个点的距离
求仙人掌直径
题解:
类比树形dp求直径。
f[i]表示i向下最多多长
处理链的话,直接dp即可。
处理环的话,类似点双tarjan,把环上的点都拉出来。
先考虑拼接更新答案。断环成链复制一倍,为了保证最短路,答案必须只能是f[i]+f[j]+i-j (i-len/2<=j<i)
单调队列优化。
直接i-j即可,另一半的绕环会在复制后的那里处理。
然后更新f[x],直接找环上其他的元素,距离就是两段距离的较小值。
因为tarjan本质上是一棵dfs树,所以处理环的时候元素都是x的儿子,儿子们的f必然已经处理。
tarjan点双时注意:
下面的写法是错误的。
因为,点双时的割点可能属于多个dcc,所以可能y不和x紧挨着存储。会弹出多余的东西。
黑色是仙人掌,红色是dfs树。A,B是V-DCC
可能访问x之后,先访问了A,因为father的dfn小,所以不能弹栈。A的红色部分在栈里保存。
然后从y进入,访问B。发现访问完了之后,可以弹栈,
如果是第二种写法,那么会等到栈顶是x才停止,那么会把A中的点也弹出来。
根本知道弹出来的是什么。。。。
第一种的话,会在弹出y之后停止。没有问题。
症结就因为x属于两个V-DCC
还要注意:
多次用queue,必须保证在l<=r时才能更新答案。
if(l<=r) ans=max(ans,f[mem[i]]+i+f[mem[q[l]]]-q[l]);
代码:
#include<bits/stdc++.h> #define reg register int #define il inline #define numb (ch^'0') using namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=500000+5; int n,m; struct node{ int nxt,to; }e[10*N]; int hd[N],cnt; void add(int x,int y){ e[++cnt].nxt=hd[x]; e[cnt].to=y; hd[x]=cnt; } int dfn[N],df,low[N]; int f[N]; int q[N],l,r; int ans; int sta[N],top,len; int mem[2*N],num; void wrk(int x){ len=num; for(reg i=1;i<=num;++i) mem[i+num]=mem[i],ans=max(ans,f[mem[i]]); l=1,r=0; for(reg i=1;i<=2*num;++i){ while(l<=r&&q[l]<i-len/2) ++l; if(l<=r) ans=max(ans,f[mem[i]]+i+f[mem[q[l]]]-q[l]); while(l<=r&&f[mem[q[r]]]-q[r]<f[mem[i]]-i) --r; q[++r]=i; } for(reg i=2;i<=num;++i){ f[x]=max(f[x],f[mem[i]]+min(i-1,num+1-i)); } } void tarjan(int x){ //cout<<" tarjan "<<x<<" top "<<top<<endl; dfn[x]=low[x]=++df; sta[++top]=x; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); if(dfn[x]<=low[y]){//find V-dcc num=0; mem[++num]=x; int z; do{ z=sta[top--]; mem[++num]=z; }while(z!=y); wrk(x); } } else low[x]=min(low[x],dfn[y]); } } int main(){ rd(n);rd(m);int k,x,y; for(reg i=1;i<=m;++i){ rd(k);rd(x); for(reg j=1;j<k;++j){ rd(y);add(x,y);add(y,x);x=y; } } tarjan(1); // for(reg i=1;i<=n;++i){ // cout<<i<<" : "<<f[i]<<endl; // } printf("%d",ans); return 0; } } int main(){ // freopen("data.in","r",stdin); // freopen("my.out","w",stdout); Miracle::main(); return 0; } /* Author: *Miracle* Date: 2018/11/30 7:44:32 */