首页 > 代码库 > [bzoj1023][SHOI2008]cactus仙人掌图

[bzoj1023][SHOI2008]cactus仙人掌图

来自FallDream的博客,未经允许,请勿转载,谢谢。


给定一个仙人掌,求最大的两点最短距离。  n<=50000

 

好难啊根本不会做。

题解:先强行dfs把图拆成树,然后我们用f[i]表示i的子树中以i为起点的最长链长度。

图中的每条边不是桥就是环上的边,对于一座桥,我们先更新一下答案,然后有f[i]=max(f[i],f[j]+1);

对于一个环,我们只在深度最低的点处理,用环上所有点的f值更新它的f。然后我们考虑最优解在环上的情况,这时我们把环拆链,然后复制一条接起来,在这上面有ans=max(f[i]+f[j]+dis(i,j))。

假设一条链标号1,2...tot...2tot,我们枚举i,这时候dis(i,j)=i-j,j比k优当且仅当f[i]+f[j]+i-j>f[i]+f[k]+i-k,也就是f[j]-j>f[k]-k,又因为我们在环上,所以长度不能超过tot/2,这个用一个单调队列维护就行了。

顺便,判断桥的方法是low[j]>dfn[i],即j不能翻到i或之上的点,无向图判环要忽略到父亲的边。

#include<iostream>
#include<cstdio>
#include<cstring>
#define MN 50000
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < 0 || ch > 9){ if(ch == -) f = -1;  ch = getchar();}
    while(ch >= 0 && ch <= 9){x = x * 10 + ch - 0;ch = getchar();}
    return x * f;
}

int q[MN+5],dn=0,tot=0,a[2*MN+5],fa[MN+5],top,tail;
int head[MN+5],n,cnt=0,m,k,f[MN+5],ans=0,dfn[MN+5],low[MN+5];
struct edge{int to,next;}e[MN*10+5];

void ins(int f,int t)
{
    e[++cnt]=(edge){t,head[f]};head[f]=cnt;
    e[++cnt]=(edge){f,head[t]};head[t]=cnt;
}

void dp(int rt,int last)
{
    for(tot=0;last!=rt;last=fa[last]) a[++tot]=f[last];
    a[++tot]=f[rt];
    for(int i=1;i<=tot;i++)a[i+tot]=a[i];
    q[top=tail=1]=1;
    for(int i=2;i<=tot<<1;i++)
    {
        while(i-q[tail]>tot/2) tail++;
        ans=max(ans,a[i]+a[q[tail]]+i-q[tail]);
        while(top>=tail&&a[i]-i>=a[q[top]]-q[top]) --top;
        q[++top]=i;
    }
    for(int i=1;i<=tot;i++) f[rt]=max(f[rt],a[i]+min(tot-i,i));
} 

void tarjan(int x,int fat)
{
    low[x]=dfn[x]=++dn;fa[x]=fat;
    for(int i=head[x];i;i=e[i].next)
    {
        if(!dfn[e[i].to]) tarjan(e[i].to,x),low[x]=min(low[x],low[e[i].to]);
        else if(e[i].to!=fat) low[x]=min(low[x],dfn[e[i].to]);
        if(e[i].to!=fat&&dfn[x]<low[e[i].to]) ans=max(ans,f[x]+f[e[i].to]+1),f[x]=max(f[x],f[e[i].to]+1);
    }
    for(int i=head[x];i;i=e[i].next)
        if(fa[e[i].to]!=x&&dfn[e[i].to]>dfn[x])
            dp(x,e[i].to);
}

int main()
{
    n=read();k=read();
    for(int i=1;i<=k;i++)
    {
        int m=read(),pre=read();
        for(int j=2;j<=m;j++)
        {
            int x=read();
            ins(x,pre);pre=x;
        }
    }
    tarjan(1,0);
    printf("%d\n",ans);
    return 0;
}

 

[bzoj1023][SHOI2008]cactus仙人掌图