Kattis - redblacktree Red Black Tree (树形背包)

问题:有一课含有n(n<=2e5)个结点的数,有m(m<=1000)个结点是红色的,其余的结点是黑色的。现从树中选若干数量的结点,其中红色的恰有k个,并且每个结点都不是其他任何另一个结点的后代,分别求出k=0,1,2,...,m的选法种数。(树根为1)

又是一道树形背包问题。只不过这个问题和普通的树形背包正好相反,选了一个结点就不能选它的祖先,但解法都是差不多的。

设dp[u][i]为在第u个结点及其子树下,红色结点数恰为i个的选法总数,则dp[u]为它的所有子结点的dp值构成的多项式的乘积加上结点本身的贡献,状态转移的时候模拟多项式的乘法就行了。

用FFT似乎也可以,但我没试,因为复杂度并不会比用siz上限优化了的直接转移要低,而且取模比较麻烦。

另外注意这道题的dp数组用long long会爆内存,只能用int存储,在运算的中间过程转化成long long。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=2e5+10,inf=0x3f3f3f3f,mod=1e9+7;
 5 int n,m,hd[N],ne,a[N],siz[N],dp[N][1000+10],b[1000+10];
 6 struct E {int v,nxt;} e[N];
 7 void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
 8 void dfs(int u) {
 9     memset(dp[u],0,sizeof dp[u]);
10     siz[u]=0,dp[u][0]=1;
11     for(int i=hd[u]; ~i; i=e[i].nxt) {
12         int v=e[i].v;
13         dfs(v);
14         for(int j=0; j<=siz[u]+siz[v]; ++j)b[j]=0;
15         for(int j=siz[u]; j>=0; --j)if(dp[u][j])
16                 for(int k=siz[v]; k>=0; --k)if(dp[v][k])
17                         b[j+k]=(b[j+k]+(ll)dp[u][j]*dp[v][k])%mod;
18         for(int j=0; j<=siz[u]+siz[v]; ++j)dp[u][j]=b[j];
19         siz[u]+=siz[v];
20     }
21     siz[u]+=a[u],dp[u][a[u]]=(dp[u][a[u]]+1)%mod;
22 }
23 
24 int main() {
25     memset(hd,-1,sizeof hd),ne=0;
26     scanf("%d%d",&n,&m);
27     for(int i=2; i<=n; ++i) {
28         int u;
29         scanf("%d",&u);
30         addedge(u,i);
31     }
32     for(int i=0; i<m; ++i) {
33         int u;
34         scanf("%d",&u);
35         a[u]=1;
36     }
37     dfs(1);
38     for(int i=0; i<=m; ++i)printf("%d\n",dp[1][i]);
39     return 0;
40 }

猜你喜欢

转载自www.cnblogs.com/asdfsag/p/10531164.html