[SHOI2008]仙人掌图 II——树形dp与环形处理

题意:

给定一个仙人掌,边权为1

距离定义为两个点之间的最短路径

直径定义为距离最远的两个点的距离

求仙人掌直径

 

题解:

类比树形dp求直径。

f[i]表示i向下最多多长

处理链的话,直接dp即可。

处理环的话,类似点双tarjan,把环上的点都拉出来。

先考虑拼接更新答案。断环成链复制一倍,为了保证最短路,答案必须只能是f[i]+f[j]+i-j (i-len/2<=j

单调队列优化。

直接i-j即可,另一半的绕环会在复制后的那里处理。

然后更新f[x],直接找环上其他的元素,距离就是两段距离的较小值。

因为tarjan本质上是一棵dfs树,所以处理环的时候元素都是x的儿子,儿子们的f必然已经处理。

 

tarjan点双时注意:

[SHOI2008]仙人掌图 II——树形dp与环形处理_第1张图片

 

 下面的写法是错误的。

因为,点双时的割点可能属于多个dcc,所以可能y不和x紧挨着存储。会弹出多余的东西。

[SHOI2008]仙人掌图 II——树形dp与环形处理_第2张图片

 

黑色是仙人掌,红色是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
#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]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]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 "<
    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;jj){
            rd(y);add(x,y);add(y,x);x=y;
        }
    }
    tarjan(1);
//    for(reg i=1;i<=n;++i){
//        cout<//    }
    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
*/

 

转载于:https://www.cnblogs.com/Miracevin/p/10044213.html

你可能感兴趣的:([SHOI2008]仙人掌图 II——树形dp与环形处理)