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;
}
Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值