BZOJ 1023: [SHOI2008]cactus仙人掌图 | 在仙人掌上跑DP

题目:

求仙人掌直径

http://www.lydsy.com/JudgeOnline/problem.php?id=1023


题解:

首先给出仙人掌的定义:满足所有的边至多在一个环上的无向联通图

我们先考虑一下如何求一棵树的直径

1.维护以每个节点的为根的子树中,以根为一条路径端点的最长路和次长路,

#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 50005
using namespace std;
inline int read()
{
    int ret=0,neg=1;char j=getchar();
    for (;j>'9' || j<'0';j=getchar()) if (j=='-') neg=-1;
    for (;j>='0' && j<='9';j=getchar()) ret=ret*10+j-'0';
    return ret*neg;
}
int n,m,ecnt,ind,ans;
int head[N],deep[N],f[N],low[N],dfn[N],fa[N];
int a[2*N],q[2*N],l,r;
struct adj{int v,nxt;}e[40*N];
void add(int u,int v)
{
    e[++ecnt].v=v;e[ecnt].nxt=head[u];head[u]=ecnt;
    e[++ecnt].v=u;e[ecnt].nxt=head[v];head[v]=ecnt;
}
void dp(int root,int x)
{
    int tot=deep[x]-deep[root]+1;
    for (int i=x;i!=root;i=fa[i])
    a[tot--]=f[i];
    a[tot]=f[root];
    tot=deep[x]-deep[root]+1;
    for (int i=1;i<=tot;i++) a[i+tot]=a[i];
    q[1]=1;l=r=1;
    for (int i=2;i<=2*tot;i++)
    {
    while (l<=r && i-q[l]>tot/2) l++;
    ans=max(ans,a[i]+i+a[q[l]]-q[l]);
    while (l<=r && a[q[r]]-q[r]<=a[i]-i) r--;
    q[++r]=i;
    }
    for (int i=2;i<=tot;i++)
    f[root]=max(f[root],a[i]+min(i-1,tot-i+1));
}
void dfs(int x)
{
    low[x]=dfn[x]=++ind;
    for (int i=head[x],v;i;i=e[i].nxt)
    if (e[i].v!=fa[x])
    {
        if (!dfn[v=e[i].v])
        {
        fa[v]=x;deep[v]=deep[x]+1;
        dfs(v);
        low[x]=min(low[x],low[v]);
        }
        else low[x]=min(low[x],dfn[v]);
        if (dfn[x]<low[v])
        ans=max(ans,f[x]+f[v]+1),f[x]=max(f[x],f[v]+1);
    }
    for (int i=head[x],v;i;i=e[i].nxt)
    if (fa[v=e[i].v]!=x && dfn[x]<dfn[v])
        dp(x,v);
}
int main()
{
    n=read();m=read();
    for (int i=1;i<=m;i++)
    {
    int k=read(),x=read();
    for (int i=2;i<=k;i++)
    {
        int y=read();add(x,y);x=y;
    }
    }
    dfs(1);
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2018-01-08 11:18  MSPqwq  阅读(160)  评论(0编辑  收藏  举报