JZOJ5233【GDOI模拟】概率博弈

来源:互联网 发布:六级英语听力软件 编辑:程序博客网 时间:2024/06/08 07:37

Description

小A和小B在玩游戏。这个游戏是这样的:
有一棵n个点的以1为根的有根树,叶子有权值。假设有m个叶子,那么树上每个叶子的权值序列就是一个1->m 的排列。
一开始在1号点有一颗棋子。两人轮流将这颗棋子移向其当前位置的一个儿子。假如棋子到达叶子,游戏结束,最终获得的权值为所在叶子对应权值。
小A希望最后的权值尽量大,小B希望尽量小。小A是先手。
在玩了很多局游戏后,小B对其中绝大多数局游戏的结果不满意,他觉得是小A对叶子权值做了手脚。于是他一怒之下,决定将叶子的权值随机排列。现在小B想知道,假如叶子的权值是随机排列的(即叶子权值的每种排列都以等概率出现),那么游戏期望的结果是多少?
请输出答案乘上m! 对10^9+7取模的结果,显然这是一个整数。

Solution

这题有一个很套路的想法,就是我们考虑最后答案大于等于k的方案数,然后用k的方案数-k+1的方案数就是等于k的方案数。
那么大于等于k的方案数的讨论也有一个很套路的想法,就是对于所有大于等于k的权值设为1,其它设为0,然后向上合并,如果最后在根节点能合并出1,那么这种01染色方案就合法,然后再给0和1表上号就好了(0的个数的阶乘和1的个数的阶乘)
那么我们就设f[i][j][0,1]表示在第i个点,以它为根的子树的叶子节点填了j个0,当前这个点的颜色为0或1。
然后我们分层数来考虑,对于奇数层(从0层开始),那么它的儿子数有一个0,当前颜色就是0的,那么我们就用总方案-全都是1的个数来求,当前要是1的话,那么就把儿子的1做个背包,枚举前面儿子的叶子然后枚举当前要合并的儿子的叶子背包一下(这样是n^2的,相当于把所有的叶子两两之间匹配一下),对于偶数层也是一样的。
最后在根节点上恰好为k的方案,然后再标号就好了。
其实还有一种求答案的方法,我们要求的期望是xP(x)
那么其实在一个平面图上就相当于1P(1),2P(2),3P(3),…nP(n),上面的方法是竖着求的,那么我们考虑横着求,设P(x)表示权值大于等于x的概率,那么答案就是P(x)和我们上面设的状态是一样的。

Code

#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)#define fod(i,a,b) for(i=a;i>=b;i--)#define rep(i,a) for(i=first[a];i;i=next[i])using namespace std;typedef long long ll;const int maxn=5007,mo=1e9+7;ll i,j,k,l,t,n,m;ll first[maxn*2],last[maxn*2],next[maxn*2],num,pp;ll ni[maxn],fact[maxn],ans,f[maxn][maxn][2],son[maxn];void add(int x,int y){    last[++num]=y,next[num]=first[x],first[x]=num;}ll qsm(ll x,ll y){    ll z=1;    for(;y;y/=2,x=x*x%mo)if(y&1)z=x*z%mo;    return z;}ll c(ll x,ll y){    return fact[x]*ni[y]%mo*ni[x-y]%mo;}void dfs(int x,int y,int z){    int i,l=0;    rep(i,x){        if(last[i]!=y){            l++;dfs(last[i],x,z+1);            fod(k,son[x]+son[last[i]],0){                pp=0;                fo(j,0,son[last[i]]){                    if(k-j<0)break;                    if(z%2)(pp+=f[last[i]][j][1]*f[x][k-j][1]%mo)%=mo;                    else(pp+=f[last[i]][j][0]*f[x][k-j][0]%mo)%=mo;                }                if(z%2)f[x][k][1]=pp;else f[x][k][0]=pp;                if(l==1){if(z%2)f[x][k][1]+=f[last[i]][k][1];else f[x][k][0]+=f[last[i]][k][0];}            }            son[x]+=son[last[i]];        }    }    if(!l)son[x]=1,f[x][0][1]=f[x][1][0]=1;    else{        fo(j,0,son[x])if(z%2)f[x][j][0]=(c(son[x],j)-f[x][j][1]+mo)%mo;else         f[x][j][1]=(c(son[x],j)-f[x][j][0]+mo)%mo;    }}int main(){    freopen("game.in","r",stdin);    freopen("game.out","w",stdout);    fact[0]=ni[0]=1;fo(i,1,5000)fact[i]=fact[i-1]*i%mo;ni[5000]=qsm(fact[5000],mo-2);    fod(i,4999,1)ni[i]=ni[i+1]*(i+1)%mo;    scanf("%lld",&n);    fo(i,1,n-1)scanf("%lld%lld",&k,&l),add(k,l),add(l,k);    dfs(1,0,0);    fo(i,0,son[1]){        ans=(ans+f[1][i][1]*fact[i]%mo*fact[son[1]-i])%mo;    }    ans=(ans+mo)%mo;    printf("%lld\n",ans);}
原创粉丝点击