CF995F Cowmpany Cowmpensation(dp+容斥)
Posted ListenSnow
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF995F Cowmpany Cowmpensation(dp+容斥)相关的知识,希望对你有一定的参考价值。
题意
一棵 \\(n\\) 个节点的树,给每个节点分配工资(\\([1, D]\\)),子节点不能超过父亲节点的工资,问有多少种分配方案。
$ 1 \\le n \\le 3000 $ , $ 1 \\le D \\le 10^9 $
思路
注意到可分配的工资数很多,但实际能用到的至多只有 \\(n\\) 种。可以考虑先记录相对大小关系,最后再乘上一个组合数。
令 \\(f_i,j\\) 表示在以 \\(i\\) 为根的子树中,节点 \\(i\\) 的权值是离散化后第 \\(j\\) 大时的合法方案数。可以得出转移方程:
其中 $\\sum_k=1^j f_v,k $ 可以用前缀和优化掉,总的转移复杂度为 \\(O(n^2)\\)。
然而,假设当前节点的权值是 \\(j\\),儿子节点的权值是 \\(k\\)。在转移的过程中,我们并不能保证 \\(k+1,k+2,\\ldots,j-1\\) 这些权值都被取到,在原始状态下这些权值不被取到是不影响正确性的,但是离散化后显然就会算重。根据题意,根节点的权值是最大的,那么 \\(f_1,i\\) 的实际定义就是至多用了 \\(i\\) 种权值,且最大值为 \\(i\\) 的方案数。
考虑容斥,设 \\(g_i\\) 为恰好用了 \\(i\\) 种权值的方案数。根据定义,其一定被 \\(f_1,i\\) 完全包含。考虑减去实际用了 \\(j\\) 种权值的方案,由于根节点一定为 \\(i\\),那么剩下的 \\(j-1\\) 种权值就一定在 \\(1 \\sim i-1\\) 中选取,于是可以得到容斥方程:
那么最终的答案就是:
code:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3010,mod=1e9+7;
int s[N][N],n,d,h[N],idx,f[N][N],ans,fac[N],g[N],inv[N],infac[N];
struct edgeint v,nex;e[N];
void add(int u,int v)e[++idx]=edgev,h[u];h[u]=idx;
void init()
fac[0]=fac[1]=infac[0]=infac[1]=inv[0]=inv[1]=1;
for(int i=2;i<N;i++) fac[i]=1ll*fac[i-1]*i%mod;
for(int i=2;i<N;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=2;i<N;i++) infac[i]=1ll*infac[i-1]*inv[i]%mod;
int C(int n,int m)if(n<0||m<0||n<m) return 0;return 1ll*fac[n]*infac[m]%mod*infac[n-m]%mod;
void Add(int &a,int b)a+=b;if(a>=mod) a-=mod;
void Sub(int &a,int b)a-=b;if(a<0) a+=mod;
void Mul(int &a,int b)a=1ll*a*b%mod;
void dfs(int u)
for(int i=h[u];i;i=e[i].nex)
int v=e[i].v;dfs(v);
for(int j=1;j<=min(n,d);j++) Mul(f[u][j],s[v][j]);
for(int i=1;i<=min(n,d);i++) s[u][i]=(s[u][i-1]+f[u][i])%mod;
int main()
scanf("%d%d",&n,&d);for(int i=1;i<=n;i++) for(int j=1;j<=min(n,d);j++) f[i][j]=1;init();
for(int u,v=2;v<=n;v++) scanf("%d",&u),add(u,v);dfs(1);g[1]=f[1][1];
for(int i=2;i<=min(n,d);i++)
g[i]=f[1][i];for(int j=1;j<i;j++) Sub(g[i],1ll*C(i-1,j-1)*g[j]%mod);
for(int i=1;i<=min(n,d);i++)
int tmp=infac[i];for(int j=d;j>=d-i+1;j--) Mul(tmp,j);//C(n,i)
Add(ans,1ll*tmp*g[i]%mod);
printf("%d\\n",ans);
return 0;
以上是关于CF995F Cowmpany Cowmpensation(dp+容斥)的主要内容,如果未能解决你的问题,请参考以下文章
CF995F Cowmpany Cowmpensation(dp+容斥)
[CF995F]Cowmpany Cowmpensation[树形dp+拉格朗日插值]