题目链接:https://www.luogu.com.cn/problem/P8352
给出一棵树,每个点的权值是 [ 1 , k ] [1,k] [1,k]之间的一个数,对于 i ∈ [ 1 , n k ] i\in[1,nk] i∈[1,nk]求令这棵树的最大独立集权值为 i i i的方案数。
1 ≤ n ≤ 1000 , 1 ≤ k ≤ 5 1\leq n\leq 1000,1\leq k\leq 5 1≤n≤1000,1≤k≤5
考虑我们求最大独立集时的 d p dp dp方程,设 f i , 0 / 1 f_{i,0/1} fi,0/1表示 i i i选/不选时的子树最大权值和。
注意到它总共有 n 2 k 2 n^2k^2 n2k2种状态,不能拿来dp套dp维护。
继续挖掘一下性质,若 f i , 0 ≥ f i , 1 f_{i,0}\geq f_{i,1} fi,0≥fi,1,那么 f i , 1 f_{i,1} fi,1显然没有用,若 f i , 0 + k ≤ f i , 1 f_{i,0}+k\leq f_{i,1} fi,0+k≤fi,1那么 f i , 0 f_{i,0} fi,0也没有用,因为我们可以显然父节点不选择更优。
所以我们可以强制 f i , 1 ∈ [ f i , 0 , f i , 0 + k ] f_{i,1}\in[f_{i,0},f_{i,0}+k] fi,1∈[fi,0,fi,0+k]这个范围,这样我们的状态数就只剩下 n k 2 nk^2 nk2种了。
设 g i , j , x g_{i,j,x} gi,j,x表示 f i , 0 = j , f i , 1 = j + x f_{i,0}=j,f_{i,1}=j+x fi,0=j,fi,1=j+x的方案数,然后 d p dp dp子树转移即可。
时间复杂度: O ( n 2 k 4 ) O(n^2k^4) O(n2k4)
#include
#include
#include
using namespace std;
const int N=1010,P=1e9+7;
struct node{
int to,next;
}a[N<<1];
int n,k,tot,siz[N],ls[N],ans[N*5],f[N][N*5][6],g[N*5][6];
void addl(int x,int y){
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;return;
}
void Add(int &x)
{x=(x>=P)?(x-P):x;}
void dfs(int x,int fa){
siz[x]=0;
for(int i=1;i<=k;i++)f[x][0][i]=1;
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
if(y==fa)continue;dfs(y,x);
for(int a=0;a<=siz[x];a++){
for(int b=0;b<=k;b++){
if(!f[x][a][b])continue;
for(int c=0;c<=siz[y];c++)
for(int d=0;d<=k;d++){
int A=a+c+d,B=a+b+c;
B=B-A;if(B<0)B=0;
Add(g[A][B]+=1ll*f[x][a][b]*f[y][c][d]%P);
}
}
}
siz[x]+=siz[y]+k;
for(int a=0;a<=siz[x];a++)
for(int b=0;b<=k;b++)
f[x][a][b]=g[a][b],g[a][b]=0;
}
return;
}
int main()
{
// printf("%d\n",sizeof(f)>>20);
// freopen("2.in","r",stdin);
// freopen("2.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
addl(x,y);addl(y,x);
}
dfs(1,0);
for(int a=0;a<=siz[1];a++)
for(int b=0;b<=k;b++)
Add(ans[a+b]+=f[1][a][b]);
for(int i=1;i<=n*k;i++)
printf("%d\n",ans[i]);
return 0;
}