Description
小A和小B在玩游戏。这个游戏是这样的:
有一棵n个点的以1为根的有根树,叶子有权值。假设有m个叶子,那么树上每个叶子的权值序列就是一个1->m 的排列。
一开始在1号点有一颗棋子。两人轮流将这颗棋子移向其当前位置的一个儿子。假如棋子到达叶子,游戏结束,最终获得的权值为所在叶子对应权值。
小A希望最后的权值尽量大,小B希望尽量小。小A是先手。
在玩了很多局游戏后,小B对其中绝大多数局游戏的结果不满意,他觉得是小A对叶子权值做了手脚。于是他一怒之下,决定将叶子的权值随机排列。现在小B想知道,假如叶子的权值是随机排列的(即叶子权值的每种排列都以等概率出现),那么游戏期望的结果是多少?
请输出答案乘上m! 对10^9+7取模的结果,显然这是一个整数。
Data Constraint
对于10%的数据,n<=5
对于30%的数据,n<=10
对于60%的数据, n<=50
对于100%的数据,n<=5000,保证给出的是一棵合法的树。
Solution
我们假设A最后取的值是k,我们令大于等于k的值为1,其余为0,那么我们做个树形dp,设f[i][j][0..1]表示以i为根的子树中,有j个1,最后取到的值为0或1。那么对于小A取的值,我们直接算出f[i][j][0]= ∏f[son[x]][k][0],f[i][j][1]= Cjsize[i]−f[i][j][0] 。对于小A取的值是k的方案即f[1][k][1]-f[1][k+1][1],由于答案是带标号的,所以我们最后要记入答案是(f[1][k][1]-f[1][k+1][1])k! (size[1]-k)!。
Code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=5e3+5,mo=1e9+7;
ll f[maxn][maxn][2],first[maxn],last[maxn*2],next[maxn*2],size[maxn];
ll n,m,i,t,j,k,l,x,y,z,q,num,c[maxn],d[maxn],ans;
ll mi(ll x,ll y){
if (y==1) return x;
ll t=mi(x,y/2);
if (y%2) return t*t%mo*x%mo;return t*t%mo;
}
ll make(int x,int y){
return c[y]*mi(c[y-x],mo-2)%mo*mi(c[x],mo-2)%mo;
}
void dg1(int t,int x,ll sum,int p,int num){
int i;
if (t>d[0]){
f[x][num][p]=(f[x][num][p]+sum)%mo;return;
}
for (i=0;i<=size[d[t]];i++)
if (f[d[t]][i][p])dg1(t+1,x,sum*f[d[t]][i][p]%mo,p,num+i);
}
void dg(int x,int y,int p){
int t;
for (t=first[x];t;t=next[t]){
if (last[t]==y)continue;
dg(last[t],x,1-p);
size[x]+=size[last[t]];
}
d[0]=0;
for (t=first[x];t;t=next[t])
if (last[t]!=y)d[++d[0]]=last[t];
if (size[x]){
dg1(1,x,1,p,0);
for (j=0;j<=size[x];j++)
f[x][j][1-p]=(make(j,size[x])-f[x][j][p]+mo)%mo;
}else size[x]++,f[x][1][1]=f[x][0][0]=1;
}
void lian(int x,int y){
last[++num]=y;next[num]=first[x];first[x]=num;
}
int main(){
freopen("game.in","r",stdin);freopen("game.out","w",stdout);
scanf("%d",&n);
for (i=1;i<n;i++)
scanf("%d%d",&x,&y),lian(x,y),lian(y,x);
c[0]=1;
for (i=1;i<=n;i++)
c[i]=c[i-1]*i%mo;
dg(1,0,0);m=size[1];
for (i=0;i<=m;i++)
ans=ans+f[1][i][1]*c[i]%mo*c[m-i]%mo;
ans=ans%mo;
printf("%lld\n",ans);
}