[CF995F]Cowmpany Cowmpensation

Posted by Dispwnl on February 28, 2019

题目

题目大意

给定一棵树,每个点要确定一个$[1,D]$之间的权值,要求每个点(除$1$节点,即根)的权值不大于它的父亲

求分配方案数

Examples

Input

3 2
1
1

Output

5

Input

3 3
1
2

Output

10

Input

2 5
1

Output

15

题解

如果$D$很小的话简单$dp$,设$f_{i,j}$表示点$i$确定权值为$j$的方案数,然后每个点子树向上转移,维护$f_i$的前缀和就好

但是$D$是$1e9$……

发现叶子节点的$f_i$可以看成一个一次函数,往上的时候是子树组合相乘出来的,相当于一个最高次为子树叶子节点数的多项式(?)

这样就可以用拉格朗日插值优化了,注意预先把逆元处理出来以减小复杂度

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
using namespace std;
const int N=3e3+5,mod=1e9+7;
struct p{
	int x,y;
}c[N];
int n,D,num;
int h[N],w[N];
int f[N][N];
int read()
{
	int x(0);
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
	return x;
}
void add(int x,int y) {c[++num]=(p){h[x],y},h[x]=num;}
void dfs(int x=1)
{
	for(int i=1;i<=n;++i)
	  f[x][i]=1;
	for(int i=h[x];i;i=c[i].x)
	  {
	  	dfs(c[i].y);
	  	for(int j=1;j<=n;++j)
	  	  f[x][j]=1ll*f[x][j]*f[c[i].y][j]%mod;
	  }
	for(int i=1;i<=n;++i)
	  {
	  	f[x][i]+=f[x][i-1];
	  	if(f[x][i]>=mod) f[x][i]-=mod;
	  }
}
int Pow(int a,int b)
{
	int ans=1;
	for(;b;b>>=1,a=1ll*a*a%mod)
	  if(b&1) ans=1ll*ans*a%mod;
	return ans;
}
int L(int x)
{
	int ans=1;
	for(int i=0;i<=n;++i)
	  if(x!=i) ans=(1ll*ans*(D-i)%mod*(x-i<0?-1:1)*w[x-i<0?i-x:x-i]%mod+mod)%mod;
	return ans;
}
void Solve()
{
	if(n>=D) return void(printf("%d",f[1][D]));
	int ans=0;
	for(int i=1;i<=n;++i)
	  w[i]=Pow(i,mod-2);
	for(int i=0;i<=n;++i)
	  {
	  	ans+=1ll*f[1][i]*L(i)%mod;
	  	if(ans>=mod) ans-=mod;
	  }
	printf("%d",ans);
}
int main()
{
	n=read(),D=read();
	for(int i=2;i<=n;++i)
	  add(read(),i);
	return dfs(),Solve(),0;
}