摧毁树状图

来源:互联网 发布:淘宝黄金戒指 编辑:程序博客网 时间:2024/04/27 22:43

摧毁树状图

有一颗 n 个节点的树,可以将树上的两条链(边不得共用,点可以)上的节点删除,问删除后最多能得到最多的连通块。

n105

吐槽
这道题可以说是树上动规的集大成之题目,细节部分实在很容易考虑不全面,不过出题人还是很良心的,给了大力搜索 60, 打表提答 80.

这种神题我怎么可能做出来,都是考完后照着大神的题解改的,不过我感觉我把代码加了些注释应该更明了一些。

预备
首先,当我们开心地敲完 80 分的暴力后,我们容易看出这是一道的树上动规,然后我们就要设计状态了。

在设计之前呢,我们首先要考虑一条链的情况:
d(u) 为点 u 的儿子数量 1 ,那么删除一条链的剩下的联通块个数为 1+ud(u),如果这条链两个端点的 LCA 不为根,那么答案还需要 +1 ,因为上方还有一个联通块。

状态设计
经过巧(wu)妙(nao)的设计,我们对一个节点记录一下状态:

  • f(u,0) :以 u 为根的子树,有一条可向上延伸的链的最大答案;
  • f(u,1) :……,不含 u 的完整链的最大答案;
  • f(u,2) :……,含 u ……
  • f(u,3) :……,一条完整的链、一条可向上延伸的链的最大答案;
  • f(u,4) :……,不含 u 的完整的两条链的最大答案;
  • f(u,5) :……,含 u ……

状态转移
状态设计一时爽,状态转移火葬场
嘛,总之状态的转移很繁琐就是了,我真的没有信心在考试的时候考虑到所有的情况(可能是我太弱了),情况非常多,代码里都注释上来,搭配一下内容应该很容易食用了。

  • f(u,0)
    • 从儿子转移上来
    • 另起炉灶
  • f(u,1)
    • 从儿子转移上来
    • f(v,2)+1
  • f(u,2)
    • 选两个 f(v,0) 拼起来
  • f(u,3)
    • 从儿子转移上来
    • 完整的链 + f(v,0)
    • 三条 f(v,0)
  • f(u,4)
    • 同一儿子转移上来
    • 不同儿子……
      • 多种情况,注意 ±1
  • f(u,5)
    • 两个 f(u,3) 拼起来
    • 四条 f(v,0)
    • 从一个儿子转移上来一整条链,然后选两条 f(v,0) 拼一起

具体实现的时候注意 mx 的初值可能为 0 ,这取决于 mx 所代表的值是否可以不选,具体见代码。

/**************************************************************    Problem: 4871    User: zhangche0526    Language: C++    Result: Accepted    Time:2936 ms    Memory:7932 kb****************************************************************/#include<iostream>#include<cstdio>#include<cstring>typedef long long ll;int read();const int MAXN=1e5+5,INF=0x0f0f0f0f;int n;struct E{int next,to;} e[MAXN<<1];int ecnt,G[MAXN];void addEdge(int u,int v){e[++ecnt]=(E){G[u],v};G[u]=ecnt;}void addEdge2(int u,int v){addEdge(u,v);addEdge(v,u);}int f[MAXN][6],scnt[MAXN];//scnt is actually sonNumber - 1;void calScnt(int u,int la){    scnt[u]=-1;    for(int i=G[u];i;i=e[i].next)        if(e[i].to!=la) ++scnt[u],calScnt(e[i].to,u);}inline void upd(int &x,const int &y){if(y>x) x=y;}void dp(int u,int la){    memset(f[u],-0x3f,sizeof(f[u]));    int i;    for(i=G[u];i;i=e[i].next) if(e[i].to!=la)        dp(e[i].to,u);//f(u,0)//{    f[u][0]=scnt[u];    for(i=G[u];i;i=e[i].next) if(e[i].to!=la)        upd(f[u][0],f[e[i].to][0]+scnt[u]);//}//f(u,1)//{    for(i=G[u];i;i=e[i].next) if(e[i].to!=la)        upd(f[u][1],f[e[i].to][2]+1),        upd(f[u][1],f[e[i].to][1]);//}//f(u,2)//{    int mx1=0;    f[u][2]=scnt[u]+1;    for(i=G[u];i;i=e[i].next) if(e[i].to!=la)        upd(f[u][2],mx1+f[e[i].to][0]+scnt[u]+1),        upd(mx1,f[e[i].to][0]);//}//f(u,3)//{//  case 1    for(i=G[u];i;i=e[i].next) if(e[i].to!=la)        upd(f[u][3],f[e[i].to][3]+scnt[u]);//  case 2    int mx2=0;mx1=-INF;    for(i=G[u];i;i=e[i].next) if(e[i].to!=la)    {        int v=e[i].to;        upd(f[u][3],mx1+f[v][0]+scnt[u]);        upd(f[u][3],f[v][1]-1+mx2+scnt[u]);        upd(f[u][3],f[v][2]-1+mx2+scnt[u]);        upd(mx1,f[v][1]-1),upd(mx1,f[v][2]-1);        upd(mx2,f[v][0]);    }   //  case 3    int mx3=0;mx1=mx2=0;    for(i=G[u];i;i=e[i].next) if(e[i].to!=la)    {        int val=f[e[i].to][0];        if(val>mx1) std::swap(mx1,val);        if(val>mx2) std::swap(mx2,val);        if(val>mx3) std::swap(mx3,val);    }    upd(f[u][3],mx1+mx2+mx3+scnt[u]);//}//f(u,4)//{//  case 1    for(i=G[u];i;i=e[i].next) if(e[i].to!=la)        upd(f[u][4],f[e[i].to][5]+1),        upd(f[u][4],f[e[i].to][4]);//  case2    mx1=-INF,mx2=-INF;    for(i=G[u];i;i=e[i].next) if(e[i].to!=la)    {        int v=e[i].to;        upd(f[u][4],f[v][1]+mx1-1);        upd(f[u][4],f[v][1]+mx2);        upd(f[u][4],f[v][2]+mx1);        upd(f[u][4],f[v][2]+mx2+1);        upd(mx1,f[v][1]),upd(mx2,f[v][2]);    }//}//f(u,5)//{//  case 1    mx1=-INF,mx2=0;    for(i=G[u];i;i=e[i].next) if(e[i].to!=la)    {        upd(f[u][5],mx1+f[e[i].to][0]+scnt[u]+1);        upd(f[u][5],mx2+f[e[i].to][3]+scnt[u]+1);        upd(mx1,f[e[i].to][3]),upd(mx2,f[e[i].to][0]);    }//  case 2    int mx4=0;mx1=mx2=mx3=0;    for(i=G[u];i;i=e[i].next) if(e[i].to!=la)    {        int val=f[e[i].to][0];        if(val>mx1) std::swap(mx1,val);        if(val>mx2) std::swap(mx2,val);        if(val>mx3) std::swap(mx3,val);        if(val>mx4) std::swap(mx4,val);    }    upd(f[u][5],mx1+mx2+mx3+mx4+scnt[u]+1);//  case 3    static int sons[MAXN];int sz=0;    sons[0]=-1;    for(i=G[u];i;i=e[i].next) if(e[i].to!=la)        sons[++sz]=e[i].to;    static int preSgMx[MAXN],preDbMx[MAXN],sufSgMx[MAXN],sufDbMx[MAXN];    preSgMx[0]=preDbMx[0]=sufSgMx[sz+1]=sufDbMx[sz+1]=0;    for(i=1;i<=sz;i++)        preDbMx[i]=std::max(preDbMx[i-1],f[sons[i]][0]+preSgMx[i-1]),        preSgMx[i]=std::max(preSgMx[i-1],f[sons[i]][0]);    for(i=sz;i;i--)        sufDbMx[i]=std::max(sufDbMx[i+1],f[sons[i]][0]+sufSgMx[i+1]),        sufSgMx[i]=std::max(sufSgMx[i+1],f[sons[i]][0]);    for(i=1;i<=sz;i++)    {        int mx=std::max(f[sons[i]][1],f[sons[i]][2]);        upd(f[u][5],mx+preDbMx[i-1]+scnt[u]);        upd(f[u][5],mx+sufDbMx[i+1]+scnt[u]);        upd(f[u][5],mx+preSgMx[i-1]+sufSgMx[i+1]+scnt[u]);    }//}}int main(){    int T=read(),x=read();    while(T--)    {        int i;        n=read();        if(x>=1) read(),read();        if(x==2) read(),read();        ecnt=0;memset(G,0,sizeof(G));        for(i=1;i<n;i++)        {            int u=read()-1,v=read()-1;            addEdge2(u,v);        }        calScnt(0,-1);        dp(0,-1);        printf("%d\n",std::max(f[0][4],f[0][5]));    }    return 0;}int read(){    char c;int flag=1,res=0;    do c=getchar();while(c!='-'&&(c<'0'||c>'9'));    if(c=='-') flag=-1;else res=c-'0';c=getchar();    while(c>='0'&&c<='9'){res=res*10+c-'0';c=getchar();}    return flag*res;}
原创粉丝点击