【树形动规】【NOIP模拟10-21】概率博弈
Description
小A和小B在玩游戏。这个游戏是这样的: 有一棵𝑛个点的以1为根的有根树,叶子有权值。假设有𝑚个叶子,那么树 上每个叶子的权值序列就是一个1 → 𝑚的排列。 一开始在1号点有一颗棋子。两人轮流将这颗棋子移向其当前位置的一个 儿子。假如棋子到达叶子,游戏结束,最终获得的权值为所在叶子对应权值。 小A希望最后的权值尽量大,小B希望尽量小。小A是先手。 在玩了很多局游戏后,小B对其中绝大多数局游戏的结果不满意,他觉得 是小A对叶子权值做了手脚。于是他一怒之下,决定将叶子的权值随机排列。现 在小B想知道,假如叶子的权值是随机排列的(即叶子权值的每种排列都以等概 率出现),那么游戏期望的结果是多少? 请输出答案乘上𝑚!对109 + 7取模的结果,显然这是一个整数。
Input Format
输入文件名为game.in。 第一行包含一个整数𝑛。 接下来𝑛 − 1行,每行两个整数𝑢, 𝑣,表示有一条连接节点𝑢, 𝑣的边。
Output Format
输出文件名为game.out。 输出一个整数,表示答案。
Sample Input & Sample Output
【输入输出样例1】 game.in game.out 5 1 2 2 3 1 4 2 5 14
【输入输出样例2】 game.in game.out 10 10 7 7 6 10 2 2 3 3 8 3 1 6 9 7 5 1 4
Solution
首先,我们设f[i][j][x]为以i为根的存在j个1的子树最终到达叶子大于等于x的方案数;
如果我们要求等于x的方案数 很显然 直接用
f[i][j][x]-f[i][j][x+1];
那我们要求期望,也就是要求结果*方案数*概率的总和
概率为1/m!,而题目中又要求将答案*m!,刚好相乘为1;
而结果*方案数=1*(f[i][j][1]-f[i][j][2]) +2*(f[i][j][2]-f[i][j][3]) ...
如上式,最终结果就是f[i][j][1]的总和;
接下来考虑如何转移,对于每个叶子,假设它大于等于x,我们将其设为1,否则为0;
然后就转化成了背包问题,对于小A,肯定尽量走1,小B尽量走0,
而只有无法走1或无法走0的情况 小A才会选择0,小B才会选择1;
所以 考虑当前子树时 我们把全是0或全是1的方案数计算出来也就是
f[i][j][0]这个状态的转移;
而f[i][j][1]直接通过总方案数-全是0或全是1的方案数得到;
最后我们就会得到f[1][j][1]也就是整棵树中存在j个1最后得到1的方案数
然后我们给这个树上的1和0编上号,所以最后答案*j的阶乘以及(m-j)的阶乘;
#include<cstdio> #include<cstring> const int yh=1e9+7; int n,ans; int head[5005],num; int f[5005][5005][2],g[5005][2]; int fac[5005]; struct edge { int next,to; }e[10005]; void add(int x,int y) { e[++num].next=head[x]; e[num].to=y; head[x]=num; } int dfs(int u,int fa,int p) { int Su=0,Sv,tot; for (int i=head[u];i;i=e[i].next) { int v=e[i].to; if (v==fa) continue; Sv=dfs(v,u,p^1); if (!Su) { memcpy(f[u],f[v],sizeof f[u]); Su+=Sv; continue; } memset(g,0,sizeof g); for (int j=0;j<=Su;j++) for (int k=0;k<=Sv;k++) { tot=j+k; g[tot][p^1]=(g[tot][p^1]+1ll*f[u][j][p^1]*f[v][k][p^1])%yh; g[tot][p]=(g[tot][p]+1ll*(f[u][j][p^1]+f[u][j][p])*(f[v][k][p^1]+f[v][k][p])-(1ll*f[u][j][p^1]*f[v][k][p^1]))%yh; } memcpy(f[u],g,sizeof f[u]); Su+=Sv; } if (!Su) { Su++; f[u][0][0]=f[u][1][1]=1; } return Su; } int main() { scanf("%d",&n); for (int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v),add(v,u); } int m=dfs(1,0,1); fac[0]=1; for (int i=1;i<=m;i++) fac[i]=(1ll*fac[i-1]*i)%yh; for (int i=1;i<=m;i++) ans=((ans+(1ll*fac[i]*fac[m-i])%yh*f[1][i][1])%yh)%yh; printf("%d",ans); }