摧毁树状图

摧毁树状图

有一颗 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
#include
#include
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;iint 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;
}

你可能感兴趣的:(题解,-省选\国赛)