蓝魔法师
分析:sz保存包括自身的所有子孙总节点数(为了求连通块的大小做准备)
dp[i][j]表示只考虑根为i的子树,包含节点i的联通块大小为j的方案个数
dp[i][0]表示dp[i][1]+...+dp[i][k]
一棵子树根节点为u的连通块(包括u)对于它的一个子节点v,当u访问到v时,假设之前已经访问了几个子节点,
之前访问的节点个数为sz[u],那么假设我们要让连通块大小为p,p可以从两个地方来:
从之前访问过的节点中,也就是sz【u】,也可以从现在正在访问的节点v,也就是sz【v】
u提供连通分量的j个 v(u的子节点,与u相连)提供k个,连起来该连通分量大小就是k+j
乘法原理方案总个数为dp[u][j]*dp[v][k];
总结:dpi,u+v=∑(dpi,u∗dpson,v)
dpi,0=∑fi,j(j<=k)
dpi,j=0,j>K
AC_Code
1 #include <iostream> 2 #include <bits/stdc++.h> 3 using namespace std; 4 typedef long long ll; 5 const int maxn = 2e3+10; 6 const int inf=0x3f3f3f3f; 7 const int mod=998244353; 8 #define rep(i,first,last) for(int i=first;i<=last;i++) 9 #define dep(i,first,last) for(int i=first;i>=last;i--) 10 struct edge{ll to,nxt;}e[maxn<<1]; 11 ll dp[maxn][maxn],sz[maxn],tmp[maxn]; 12 ll n,m,u,v,cnt,head[maxn<<1]; 13 void addedge(ll u,ll v){ 14 e[cnt].to=v; 15 e[cnt].nxt=head[u]; 16 head[u]=cnt++; 17 } 18 void add(ll &x,ll y){ x=(x+y>=mod?x+y-mod:x+y);} 19 void dfs(ll u,ll fa){ 20 sz[u]=1; 21 dp[u][1]=1; 22 for(ll i=head[u];~i;i=e[i].nxt){ 23 ll v=e[i].to; 24 if( v!=fa ){ 25 dfs(v,u); 26 rep(j,1,sz[u]+sz[v]) tmp[j]=0;//初始化 27 rep(j,0,sz[u]){ 28 rep(k,0,sz[v]){ 29 add(tmp[j+k],1ll*dp[v][k]*dp[u][j]%mod); 30 } 31 } 32 sz[u]+=sz[v]; 33 rep(j,1,sz[u]) dp[u][j]=tmp[j]; 34 } 35 } 36 dp[u][0]=0; 37 rep(j,1,m) add(dp[u][0],dp[u][j]); 38 rep(j,m+1,sz[u]) dp[u][j]=0; 39 } 40 int main() 41 { 42 memset(head,-1,sizeof(head)); 43 scanf("%lld%lld",&n,&m); 44 rep(i,1,n-1){ 45 ll u,v; 46 scanf("%lld%lld",&u,&v); 47 addedge(u,v); 48 addedge(v,u); 49 } 50 dfs(1,0); 51 printf("%lld\n",dp[1][0]); 52 return 0; 53 }