bzoj1023 仙人掌图【仙人掌&&树形dp】

解题思路:

f[i]表示以i为根的子树中i为起点的最长链。

若(u,v)是桥,则ans=max(ans,f[u]+f[v]+1)(此时f[u]还未被f[v]更新),接着f[u]=max(f[u],f[v]+1);这也是树的直径的一种解法。

若u是环上的一个点,设该环大小为cnt,则ans=max(ans,f[u]+f[v]+dis(u,v))其中v也为该环上一点且dis(u,v)<=cnt/2(不然就走环的另一侧了),所以可以用单调队列维护dp。
处理完一个环后,假设u是dfs时进入该环的点,那么f[u]=max(f[v]+dis(v,u)),其中v为该环上一点且dis(u,v)<=cnt/2。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<ctime>
#include<vector>
#include<queue>
#define ll long long
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=50005;
int n,m,k,ans;
vector<int>g[N];
int idx,dfn[N],low[N],fa[N];
int f[N],a[N<<1],q[N<<1];

void dp(int root,int u)
{
    int cnt=0;
    while(u!=root)a[++cnt]=f[u],u=fa[u];
    a[++cnt]=f[root];reverse(a+1,a+cnt+1);
    for(int i=cnt+1;i<=cnt*2;i++)a[i]=a[i-cnt];
    int head=1,tail=1;
    q[tail]=1;
    for(int i=2;i<=cnt+cnt/2;i++)
    {
        while(head<tail&&q[head]<i-cnt/2)head++;
        ans=max(ans,a[i]+a[q[head]]+i-q[head]);
        while(head<tail&&a[i]-i>a[q[tail]]-q[tail])tail--;
        q[++tail]=i;
    }
    for(int i=2;i<=cnt;i++)
        f[root]=max(f[root],a[i]+min(i-1,cnt-i+1));
}

void tarjan(int u)
{
    dfn[u]=low[u]=++idx;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(v==fa[u])continue;
        if(!dfn[v])
        {
            fa[v]=u;
            tarjan(v);
            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[u],f[v]+1);
    }
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(fa[v]!=u&&dfn[u]<dfn[v])
            dp(u,v);
    }
}

int main()
{
    //freopen("lx.in","r",stdin);
    //freopen("lx.out","w",stdout);
    int x,y;
    n=getint(),m=getint();
    while(m--)
    {
        k=getint()-1;
        x=getint();
        while(k--)
        {
            y=getint();
            g[x].push_back(y),g[y].push_back(x);
            x=y;
        }
    }
    tarjan(1);
    cout<<ans<<'\n';
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值