zoukankan      html  css  js  c++  java
  • SHOI2008仙人掌图(tarjan+dp)

    Solution

    好题啊没的说。

    本题需要求出仙人掌的直径,但仙人掌是一个带有简单环的一张图无法直接用树形dp求解,但它有一个好东西就是没有类似环套环的东西,所以我们在处理时就方便了一些。

    思路:tarjan找环,对于不在环上的边或点,树形dp求解,对于每个环,dp求解(单调队列优化),

    下面主要说一下代码的实现,

    void tarjan(int u,int ff)
    {
    dfn[u]=low[u]=++top;
    deep[u]=deep[ff]+1;
    for(int i=head[u];i;i=an[i].n)
    if(an[i].to!=ff)
    {
    int v=an[i].to;
    if(!dfn[v])
    {
    fa[v]=u;
    tarjan(v,u);
    low[u]=min(low[u],low[v]);
    }
    else low[u]=min(low[u],dfn[v]);
    if(dfn[u]<low[v])
    {
    ans=max(ans,f[u]+f[v]+1);
    f[u]=max(f[v]+1,f[u]);
    }
    }
    for(int i=head[u];i;i=an[i].n)
    if(an[i].to!=ff&&fa[an[i].to]!=u&&dfn[an[i].to]>dfn[u])
    ddpp(u,an[i].to);
    }


    这里是tarjan找环的部分,和普通的tarjan不一样的是,由于我们不用缩点并且仙人掌图没有横叉边,所以并不用开栈存点,当dfn[u]<low[v]时,说明这条边不在环上,可以用树形dp,下面用来判断当u
    是环上一点并且是环上点中dfn序最小的点,这是就可以进行dp了(这是为了重)。

    inline void ddpp(int s,int t)
    {
    int len=deep[t]-deep[s]+1,te=len;
    for(int i=t;i!=s;i=fa[i])
    c[te--]=i;
    c[1]=s;
    for(int i=1;i<=len;++i)
    c[i+len]=c[i];
    int h=t=1;q[h]=1;
    for(int i=2;i<=len*2;++i)
    {
    while(h<=t&&(i-q[h])>len/2)h++;
    if(h<=t)ans=max(ans,f[c[q[h]]]+f[c[i]]+i-q[h]);
    while(h<=t&&f[c[q[t]]]-q[t]<=f[c[i]]-i)t--;
    q[++t]=i;
    } 
    for(int i=2;i<=len;++i)
    f[s]=max(f[s],f[c[i]]+min(len-i+1,i-1));
    }


    单调队列优化dp,用到了断环成链的技巧,要注意dp结束后要用环上所有点来更新s点,因为除了s点外其他点以后都没有用了,但s点还要向上更新,这步操作相当于把环缩成一个点。

    完整代码

    #include<iostream>
    #include<cstdio>
    #define N 50009
    #define M 2000009
    using namespace std;
    int dfn[N],low[N],ans,deep[N],c[N<<1],q[N<<1],f[N],tot,head[N],top,fa[N],u,v,n,m,k;
    struct ef
    {
    int n,to;
    }an[M];
    inline void add(int u,int v)
    {
    an[++tot].n=head[u];
    an[tot].to=v;
    head[u]=tot;
    }
    inline void ddpp(int s,int t)
    {
    int len=deep[t]-deep[s]+1,te=len;
    for(int i=t;i!=s;i=fa[i])
    c[te--]=i;
    c[1]=s;
    for(int i=1;i<=len;++i)
    c[i+len]=c[i];
    int h=t=1;q[h]=1;
    for(int i=2;i<=len*2;++i)
    {
    while(h<=t&&(i-q[h])>len/2)h++;
    if(h<=t)ans=max(ans,f[c[q[h]]]+f[c[i]]+i-q[h]);
    while(h<=t&&f[c[q[t]]]-q[t]<=f[c[i]]-i)t--;
    q[++t]=i;
    } 
    for(int i=2;i<=len;++i)
    f[s]=max(f[s],f[c[i]]+min(len-i+1,i-1));
    }
    void tarjan(int u,int ff)
    {
    dfn[u]=low[u]=++top;
    deep[u]=deep[ff]+1;
    for(int i=head[u];i;i=an[i].n)
    if(an[i].to!=ff)
    {
    int v=an[i].to;
    if(!dfn[v])
    {
    fa[v]=u;
    tarjan(v,u);
    low[u]=min(low[u],low[v]);
    }
    else low[u]=min(low[u],dfn[v]);
    if(dfn[u]<low[v])
    {
    ans=max(ans,f[u]+f[v]+1);
    f[u]=max(f[v]+1,f[u]);
    }
    }
    for(int i=head[u];i;i=an[i].n)
    if(an[i].to!=ff&&fa[an[i].to]!=u&&dfn[an[i].to]>dfn[u])
    ddpp(u,an[i].to);
    }
    int main()
    {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
    scanf("%d%d",&k,&u);
    for(int j=1;j<k;++j)
    {
    scanf("%d",&v);
    add(u,v),add(v,u);
    u=v;
    }
    }
    tarjan(1,0);
    cout<<ans;
    return 0;
    }
  • 相关阅读:
    December 23rd 2016 Week 52nd Friday
    December 22nd 2016 Week 52nd Thursday
    December 21st 2016 Week 52nd Wednesday
    December 20th 2016 Week 52nd Tuesday
    December 19th 2016 Week 52nd Sunday
    December 18th 2016 Week 52nd Sunday
    uva294(唯一分解定理)
    uva11624Fire!(bfs)
    fzu2150Fire Game(双起点bfs)
    poj3276Face The Right Way
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/9389183.html
Copyright © 2011-2022 走看看