hdu7115 Vertex Deletion

https://acm.hdu.edu.cn/showproblem.php?pid=7115

 

题意:

一棵树,删点。要求不能删出孤立的点。问有多少种删除的方案。

 

一道挺简单的树形dp,可惜比赛的时候没看这个题

 

f[i][0]表示i的子树中,点i删除的方案数。要求i的剩余子树是合法的删点。

f[i][1]表示i的子树中,点i没有删除,i的直接子节点全删除了。这个状态的i虽然暂时是孤立的,但是还有i的父节点。它的存在说明如果要使用这个状态,父节点不可以删除。

f[i][2]表示i的子树中,点i没有删除,i的直接子节点还有没删除的。要求i的剩余子树是合法的删点。

为什么这么定义状态?

至少我们要表示出点i的子树中,i有没有删除这两种状态

若没有删除点i,如果i的直接子节点全删除了,要求i的父节点不能删除;如果i的直接子节点还有没删除的,父节点随意

若删除了点i,要求i的直接子节点还有子节点没有删除

理清了这个,转移方程也出来了

f[i][0] = Π(f[t][0]+f[t][2]) ,点i删除了,那么i的直接子节点不能成为孤立的点。

f[i][1] = Πf[t][0],点i没有删除,同时要求删除i的全部直接子节点。

f[i][2] = Π(f[t][0]+f[t][1]+f[t][2]) - f[i][1],点i没有删除,还与直接子节点相连的方案数,用子节点的全部情况减去子节点全删除的方案数

 

#include<bits/stdc++.h>

using namespace std;

#define N 100003

int front[N],to[N<<1],nxt[N<<1],tot; 
int f[N][3];

const int mod=998244353;

void add(int u,int v)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot;
}

void dfs(int x,int fa)
{
    int t;
    f[x][0]=1;
    f[x][1]=1;
    f[x][2]=1;
    for(int i=front[x];i;i=nxt[i])
    {
        t=to[i];
        if(t==fa) continue;
        dfs(t,x);
        f[x][0]=1ll*f[x][0]*(f[t][0]+f[t][2])%mod;
        f[x][1]=1ll*f[x][1]*f[t][0]%mod;
        f[x][2]=1ll*f[x][2]*((f[t][0]+f[t][1])%mod+f[t][2])%mod;
    }
    f[x][2]=(f[x][2]-f[x][1]+mod)%mod;
}

int main()
{
    int T,n,u,v;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        tot=0;
        for(int i=1;i<=n;++i) front[i]=0;
        for(int i=1;i<n;++i) 
        {
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        dfs(1,0);
        printf("%d\n",(f[1][0]+f[1][2])%mod);
    }
}

 

posted @ 2021-09-05 15:09  TRTTG  阅读(89)  评论(0编辑  收藏  举报