正题
题目链接:https://www.luogu.com.cn/problem/P8352
题目大意
给出一棵树,每个点的权值是[1,k][1,k][1,k]之间的一个数,对于i∈[1,nk]i\in[1,nk]i∈[1,nk]求令这棵树的最大独立集权值为iii的方案数。
1≤n≤1000,1≤k≤51\leq n\leq 1000,1\leq k\leq 51≤n≤1000,1≤k≤5
解题思路
考虑我们求最大独立集时的dpdpdp方程,设fi,0/1f_{i,0/1}fi,0/1表示iii选/不选时的子树最大权值和。
注意到它总共有n2k2n^2k^2n2k2种状态,不能拿来dp套dp维护。
继续挖掘一下性质,若fi,0≥fi,1f_{i,0}\geq f_{i,1}fi,0≥fi,1,那么fi,1f_{i,1}fi,1显然没有用,若fi,0+k≤fi,1f_{i,0}+k\leq f_{i,1}fi,0+k≤fi,1那么fi,0f_{i,0}fi,0也没有用,因为我们可以显然父节点不选择更优。
所以我们可以强制fi,1∈[fi,0,fi,0+k]f_{i,1}\in[f_{i,0},f_{i,0}+k]fi,1∈[fi,0,fi,0+k]这个范围,这样我们的状态数就只剩下nk2nk^2nk2种了。
设gi,j,xg_{i,j,x}gi,j,x表示fi,0=j,fi,1=j+xf_{i,0}=j,f_{i,1}=j+xfi,0=j,fi,1=j+x的方案数,然后dpdpdp子树转移即可。
时间复杂度:O(n2k4)O(n^2k^4)O(n2k4)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
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;
}