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\\) 大时的合法方案数。可以得出转移方程:

\\[f_i,j=\\prod_v \\in son_i\\left ( \\sum_k=1^j f_v,k \\right ) \\]

其中 $\\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\\) 中选取,于是可以得到容斥方程:

\\[g_i=f_1,i-\\sum_j=1^i-1 \\binomi-1j-1 g_j \\]

那么最终的答案就是:

\\[\\sum_i=1^\\min(n,d) \\binomdi g_i \\]

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+拉格朗日插值]

Codeforces 995F Cowmpany Cowmpensation - 组合数学

关于安排

CF 爆发者

[CF930E]/[CF944G]Coins Exhibition