2651: 城市改建 树形DP

我太sb了。。一看输出方案就瞎jb记录了一坨信息。。最后发现根本没有用。。
结果写了6.7K。。。成功成为了BZOJ写的最长跑的最慢的选手2333。。


题目即在一棵树上删一边加一边,使得新树的直径最小。
那么我们就要维护直径相关的信息。。于是大力DP。。
首先自底向上DP,设 fi 表示以节点 i 为根的子树的直径,gi表示以节点 i 为根的子树中离i最远的点的距离。
假设 j,k i 的孩子,且jk
那么有

gi=max{gj+1}

fi=max{fj,gj+1,gj+gk+2}

然后我们自顶向下DP,类似的,设 hi 表示除去以 i 为根的子树后,剩余树的直径,pi表示除去以 i 为根的子树后,离i最远点的距离。
j,k i 的兄弟,且jk
那么有
pi=max{pfai+1,gj+2}

hi=max{hfai,pfai+gj+1,fj,gj+gk+2}

然后就记录一些前缀max后缀max最大次大什么的转移就行了。
至于记录方案。。只要把 f,g 相关的转移记录下来就好了,至于 p,h 根本用不到。多写了一坨懒得删了。

然后我们枚举删去某条边 (i,fai) ,那么有

ans=max{fi,hi,fi+12+hi+12+1}

最后 BFS 一下记录方案。
时间复杂度 O(n)

附sb代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;

const int N=300005;
const int inf=1000000007;
int n,cnt,ans,id;
int head[N],list[N<<1],next[N<<1];
int premx[N],sufmx[N],preid[N],sufid[N],fa[N],f[N],g[N],h[N],p[N],q[N];
pair<int,int> pref[N],preh[N];
int preg[N],prep[N],pre[N],path[N];
bool vis[N];

inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}

inline void insert(int x,int y)
{
    next[++cnt]=head[x];
    head[x]=cnt;
    list[cnt]=y;
}

inline int GETANS(int i)
{
    return max(max((f[i]+1)/2+(h[i]+1)/2+1,f[i]),h[i]);
}

inline void clear(int tot)
{
    for (int i=1;i<=tot+1;i++) sufmx[i]=premx[i]=-inf;
}

void DP1(int x)
{
    preg[x]=x,pref[x]=make_pair(x,x);
    for (int i=head[x];i;i=next[i])
        if (list[i]!=fa[x])
        {
            fa[list[i]]=x;
            DP1(list[i]);
        }
    int mx1=-inf,mx2=-inf,j1,j2;
    for (int i=head[x];i;i=next[i])
        if (list[i]!=fa[x])
        {
//          g[x]=max(g[x],g[list[i]]+1);
            if (g[list[i]]+1>g[x]) g[x]=g[list[i]]+1,preg[x]=preg[list[i]];

//          f[x]=max(f[x],f[list[i]]);
            if (f[list[i]]>f[x]) f[x]=f[list[i]],pref[x]=pref[list[i]];

            if (g[list[i]]>mx1) mx2=mx1,mx1=g[list[i]],j2=j1,j1=preg[list[i]];
            else if (g[list[i]]>mx2) mx2=g[list[i]],j2=preg[list[i]];
        }
//  f[x]=max(f[x],g[x]);
    if (g[x]>f[x]) f[x]=g[x],pref[x]=make_pair(preg[x],x);

    if (mx1!=-inf&&mx2!=-inf) 
//      f[x]=max(f[x],mx1+mx2+2);
        if (mx1+mx2+2>f[x])
            f[x]=mx1+mx2+2,pref[x]=make_pair(j1,j2);
}

void DP2(int x)
{
    vector<int> q;
    q.push_back(0);
    for (int i=head[x];i;i=next[i])
        if (list[i]!=fa[x]) q.push_back(list[i]);
//  cout << x << " : ";
//  for (int i=1;i<=tot;i++) cout<< q[i] << " " ;
//  cout << endl;
    int tot=q.size()-1;
    for (int i=1;i<=tot;i++)
    {
//      h[q[i]]=max(h[q[i]],h[x]);
        if (h[x]>h[q[i]]) h[q[i]]=h[x],preh[q[i]]=preh[x];

//      p[q[i]]=max(p[q[i]],p[x]+1);
        if (p[x]+1>p[q[i]]) p[q[i]]=p[x]+1,prep[q[i]]=prep[x];
    }
    clear(tot);
    for (int i=1;i<=tot;i++)
//      premx[i]=max(premx[i-1],f[q[i]]);
        if (f[q[i]]>premx[i-1]) 
            premx[i]=f[q[i]],preid[i]=q[i];
        else premx[i]=premx[i-1],preid[i]=preid[i-1];
    for (int i=tot;i;i--)
//      sufmx[i]=max(sufmx[i+1],f[q[i]]);
        if (f[q[i]]>sufmx[i+1])
            sufmx[i]=f[q[i]],sufid[i]=q[i];
        else sufmx[i]=sufmx[i+1],sufid[i]=sufid[i+1];
    for (int i=2;i<=tot;i++)
//      h[q[i]]=max(h[q[i]],premx[i-1]);
        if (premx[i-1]>h[q[i]]) 
            h[q[i]]=premx[i-1],preh[q[i]]=pref[preid[i-1]];
    for (int i=1;i<tot;i++)
//      h[q[i]]=max(h[q[i]],sufmx[i+1]);
        if (sufmx[i+1]>h[q[i]])
            h[q[i]]=sufmx[i+1],preh[q[i]]=pref[sufid[i+1]];
    clear(tot);
    for (int i=1;i<=tot;i++)
//      premx[i]=max(premx[i-1],g[q[i]]);
        if (g[q[i]]>premx[i-1]) premx[i]=g[q[i]],preid[i]=q[i];
        else premx[i]=premx[i-1],preid[i]=preid[i-1];
    for (int i=tot;i;i--)
//      sufmx[i]=max(sufmx[i+1],g[q[i]]);
        if (g[q[i]]>sufmx[i+1]) sufmx[i]=g[q[i]],sufid[i]=q[i];
        else sufmx[i]=sufmx[i+1],sufid[i]=sufid[i+1];
    for (int i=2;i<=tot;i++)
//      p[q[i]]=max(p[q[i]],premx[i-1]+2);
        if (premx[i-1]+2>p[q[i]]) 
            p[q[i]]=premx[i-1]+2,prep[q[i]]=preg[preid[i-1]];
    for (int i=1;i<tot;i++)
//      p[q[i]]=max(p[q[i]],sufmx[i+1]+2);
        if (sufmx[i+1]+2>p[q[i]])
            p[q[i]]=sufmx[i+1]+2,prep[q[i]]=preg[sufid[i+1]];
    if (tot==1) 
//      h[q[1]]=max(h[q[1]],p[x]);
        if (p[x]>h[q[1]]) 
            h[q[1]]=p[x],preh[q[1]]=make_pair(prep[x],x);
    for (int i=2;i<=tot;i++)
//      h[q[i]]=max(h[q[i]],p[x]+premx[i-1]+1);
        if (p[x]+premx[i-1]+1>h[q[i]])
            h[q[i]]=p[x]+premx[i-1]+1,preh[q[i]]=make_pair(preg[preid[i-1]],prep[x]);
    for (int i=1;i<tot;i++) 
//      h[q[i]]=max(h[q[i]],p[x]+sufmx[i+1]+1);
        if (p[x]+sufmx[i+1]+1>h[q[i]])
            h[q[i]]=p[x]+sufmx[i+1]+1,preh[q[i]]=make_pair(preg[sufid[i+1]],prep[x]);
    for (int i=2;i<tot;i++)
//      h[q[i]]=max(h[q[i]],premx[i-1]+sufmx[i+1]+2);
        if (premx[i-1]+sufmx[i+1]+2>h[q[i]])
            h[q[i]]=premx[i-1]+sufmx[i+1]+2,preh[q[i]]=make_pair(preg[preid[i-1]],preg[sufid[i+1]]);
    int mx1,mx2,j1,j2;
    mx1=-inf; mx2=-inf;
    for (int i=1;i<=tot;i++)
    {
        if (mx1!=-inf&&mx2!=-inf) 
            //h[q[i]]=max(h[q[i]],mx1+mx2+2);
            if (mx1+mx2+2>h[q[i]])
                h[q[i]]=mx1+mx2+2,preh[q[i]]=make_pair(j1,j2);
        if (g[q[i]]>mx1) mx2=mx1,mx1=g[q[i]],j2=j1,j1=preg[q[i]];
        else if (g[q[i]]>mx2) mx2=g[q[i]],j2=preg[q[i]];
    }
    mx1=-inf; mx2=-inf;
    for (int i=tot;i;i--)
    {
        if (mx1!=-inf&&mx2!=-inf) 
            //h[q[i]]=max(h[q[i]],mx1+mx2+2);
            if (mx1+mx2+2>h[q[i]])
                h[q[i]]=mx1+mx2+2,preh[q[i]]=make_pair(j1,j2);
        if (g[q[i]]>mx1) mx2=mx1,mx1=g[q[i]],j2=j1,j1=preg[q[i]];
        else if (g[q[i]]>mx2) mx2=g[q[i]],j2=preg[q[i]];
    }
    for (int i=1;i<=tot;i++) DP2(q[i]);
}

inline void BFS(int x)
{
    memset(vis,0,sizeof(vis));
    memset(path,0,sizeof(path));
    queue<int> q;
    q.push(x); vis[x]=true;
    while (!q.empty())
    {
        int x=q.front(); q.pop();
//      cout << x << " ";
        for (int i=head[x];i;i=next[i])
            if (!vis[list[i]])
            {
                vis[list[i]]=1;
                path[list[i]]=x;
                q.push(list[i]);
            }
    }
}

inline int find(int x,int ff)
{
//  cout << "now : " ;
    fa[x]=ff; DP1(x);
    int st=pref[x].first,en=pref[x].second;
//  cout << st <<  " " << en <<endl;
    BFS(st);
//  for (int i=1;i<=n;i++) cout << path[i] << " ";
//  puts("");
    int cnt=0;
    int i;
    for (i=en;i;i=path[i])
    {
//      cout << i << " ";
        if (++cnt==f[x]/2+1) break;
    }
//  cout << endl;
    return i;
}

int main()
{
//  freopen("A1316.out","w",stdout);

    n=read();
    for (int i=1;i<n;i++)
    {
        int u=read(),v=read();
        insert(u,v); insert(v,u);
    }
    DP1(1); DP2(1);

//  for (int i=1;i<=n;i++) cout << p[i] <<" ";
//  cout << endl;

    ans=inf;
    for (int i=1;i<=n;i++)
//      ans=min(ans,max(max((f[i]+1)/2+(h[i]+1)/2+1,f[i]),h[i]));
        if (GETANS(i)<ans) ans=GETANS(i),id=i;
    int A=id,B=fa[id];
    memset(fa,0,sizeof(fa));
    memset(f,0,sizeof(f));
    memset(g,0,sizeof(g));
    printf("%d\n%d %d\n%d %d\n",ans,A,B,find(A,B),find(B,A));
//  for (int i=1;i<=n;i++)
//      cout << i << " " << fa[i] << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值