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); } }