P10053 题解
思路
考虑树形 DP,提前处理出每个结点处释放的球数
设
因此我们在处理
考虑转移,首先发现这
另外还要确定的就是在
那么从父亲来的
由于从父亲来的
- 从
a_i 个中选出k 个落入to 子树。 - 从落入
to 子树的共(k+y) 个球中选出k 个作为从i 释放的,并确定顺序。 - 从落入
ano 子树的共(a_i-k+j-y) 个球中选出(a_i-k) 个作为从i 释放的,并确定顺序。 - 两个子树内部的方案数。
所以对于给定
最终把所有
代码
#include<iostream>
#define int long long
using namespace std;
const int N=4e3+10,mod=1e9+7;
int po(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod,b>>=1;
}
return res;
}
int fr[N],ifr[N];
int C(int n,int m) {return fr[n]*ifr[m]%mod*ifr[n-m]%mod;}
int A(int n,int m) {return fr[n]*ifr[n-m]%mod;}
int temp,n,kk,a[N],b[N],l[N],r[N],siz[N],cnt[N],f[N][N];
void dfs(int i,bool lr)
{
int lc=l[i],rc=r[i];
if(lc) dfs(lc,1);
if(rc) dfs(rc,0);
siz[i]=siz[lc]+siz[rc]+1,cnt[i]=cnt[lc]+cnt[rc]+a[i],b[i]=siz[i]-cnt[i];
for(int j=0;j<=b[i]&&j<=kk-cnt[i];j++)
{
int to=(lr?lc:rc),ano=(lr?rc:lc);
for(int k=0;k<=b[to]&&k<=a[i];k++)
{
int y=min(j,b[to]-k),flag=(j==b[i]);
int ta=f[to][k+y]*A(k+y,k)%mod,tb=f[ano][a[i]-k+j-y-flag]*A(a[i]-k+j-y,a[i]-k)%mod;
f[i][j]=(f[i][j]+C(a[i],k)*ta%mod*tb%mod)%mod;
}
}
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>kk,fr[0]=ifr[0]=f[0][0]=1;
for(int i=1;i<=n;i++) fr[i]=fr[i-1]*i%mod,ifr[i]=po(fr[i],mod-2);
for(int i=1;i<=kk;i++) cin>>temp,a[temp]++;
for(int i=1;i<=n;i++) cin>>l[i]>>r[i];
dfs(1,0),cout<<f[1][0];
return 0;
}