[BZOJ1023]cactus仙人掌图——仙人掌直径

[BZOJ1023][SHOI2008]cactus仙人掌图

        仙人掌的直径。不在环上的边按照树处理,在环上的边先留下来,把从环上分岔处理完,再用单调队列环形DP处理。

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10;
const int M=1e6+10;
int head[N],ver[2*M],nex[2*M],tot=1;
inline void add(int x,int y) {
    ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
}
int dfn[N],low[N],num,stk[N],top,d[N],ans;
int c[2*N],v[2*N],cnt,q[2*N];
void solve_circle(){//单调队列处理环形DP
    int len=cnt;
    cnt=2*cnt;
    for(int i=len;i<cnt;++i)c[i]=c[i-len];
    int st=0,ed=-1;
    q[++ed]=0;v[0]=d[c[0]];
    for(int j=1;j<cnt;++j){
        v[j]=d[c[j]]-j;
        if(q[ed]-q[st]+1>(len>>1))++st;//len>>1因为要走环上短的一侧
        ans=max(ans,j+d[c[j]]+v[q[st]]);
        while(st<=ed&&v[q[ed]]<=v[j])--ed;
        q[++ed]=j;
    }
    int Max=0;
    for(int i=0;i<len-1;++i)
        Max=max(Max,min(len-1-i,i+1)+d[c[i]]);
    d[c[len-1]]=max(d[c[len-1]],Max);
}
void tarjan(int x,int fa) {
    dfn[x]=low[x]=++num;
    stk[++top]=x;
    for(int i=head[x]; i; i=nex[i]) {
        int y=ver[i];
        if(!dfn[y]) {
            tarjan(y,x);
            low[x]=min(low[x],low[y]);
            if(low[y]>dfn[x]) {
                --top;
                ans=max(ans,d[x]+d[y]+1);
                d[x]=max(d[x],d[y]+1);
            }else if(low[y]==dfn[x]){
                cnt=0;
                do{
                    c[cnt++]=stk[top];//保存环
                } while (stk[top--]!=y);
                c[cnt++]=x;
                solve_circle();
            }
        } else if(y!=fa)
            low[x]=min(low[x],dfn[y]);
    }
}
int main(){
    int n,m,k,x,y;
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;++i){
        scanf("%d%d",&k,&x);
        while(--k){
            scanf("%d",&y);
            add(x,y);add(y,x);
            x=y;
        }
    }
    tarjan(1,0);
    printf("%d",ans);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/zpengst/p/12520272.html