[概率期望DP]JZOJ 4212 我想大声告诉你

Description

因为小Y 是知名的白富美,所以自然也有很多的追求者,这一天这些追求者打算进行一次游戏来踢出一些人,小R 自然也参加了。
这个游戏有n 个人参加,每一轮随机选出一个还没有出局的人x,接着x 会出局。x 在出局之后剩下的人会受到一次攻击,每一个人在遭到攻击之后会有p 的概率出局。(注意遭到攻击出局的人是不能攻击剩下的人的)
在所有人都出局之后,遭受攻击次数等于特定值的人能够成为胜者。所以现在小R 想要知道对于每一个0 <= k < n,自己恰好在遭受k 次攻击之后出局的概率是多少。(这里的出局指的不是被攻击出局)
注意在这题中,所有数值的运算在模258280327 的意义下进行。
 

Input

第一行输入一个正整数T 表示数据组数。
对于每一组数据输入仅一行三个数n, x, y,表示在这组数据中有n 个人参赛,p = x/y。保证y 和258280327 互质。

Output

对于每组数据,输出一行n 个整数,表示对于k = 0到n - 1 的概率在模258280327 意义下的值。
 

Sample Input

2
3 40 100
9 32 1049

Sample Output

172186885 92980918 16529941
229582513 163885050 39458156 102374877 116777758 216371874 55544199 95860736 8136787
 

Data Constraint

对于60% 的数据,n <=100
对于100% 的数据,n <= 2* 10^3,1 <= T <= 5,0<= x < y <= 10^9

分析

我想大声挖倍内听!吔X啦梁非凡!

内!我要炒内啊!

↑这是我看到题目一瞬间脑补的

这题意有个等价的方法

就是第i次游戏选第i个人,第i个人未出局就出局,并且使后面i+1~n个人有p概率出局,已出局就不管

其实是一样的

那么可以写DP

设f[i][j]为选第i个人前,1~i-1有j个人被点出局了

那么状态转移显然:

1、f[i+1][j+1]+=f[i][j]*((y-x)/y)^(j+1)(i被点掉了)

2、f[i+1][j]+=f[i][j]*(x/y)^(j)(i刚好没了)

最后求和除个n就行

#include <iostream>
#include <cstdio>
#include <memory.h>
using namespace std;
typedef long long ll;
const int P=258280327;
const int N=2e3+10;
int T,n;
ll x,y,ny[N],f[N][N];

ll Power(ll x,ll y) {ll ans=1;for (;y;y>>=1,(x*=x)%=P) if (y&1) (ans*=x)%=P;return ans;}

int main() {
    for (scanf("%d",&T);T;T--) {
        scanf("%d%lld%lld",&n,&x,&y);
        ll cy=Power(y,P-2);ny[0]=1;memset(f,0,sizeof f);f[1][0]=1;
        for (int i=1;i<=n;i++) ny[i]=ny[i-1]*(y-x)%P*cy%P;
        for (int i=1;i<n;i++)
            for (int j=0;j<i;j++)
                if (f[i][j])
                    (f[i+1][j+1]+=f[i][j]*ny[j+1]%P)%=P,(f[i+1][j]+=f[i][j]*(1-ny[j]+P)%P)%=P;
        for (int i=0;i<n;i++) {
            ll lans=0;
            for (int j=1;j<=n;j++) (lans+=f[j][i])%=P;
            printf("%lld ",lans*Power(n,P-2)%P);
        }
        printf("
");
    }
}
View Code
在日渐沉没的世界里,我发现了你。
原文地址:https://www.cnblogs.com/mastervan/p/10316836.html