【2019/07/20测试 T2】切题

传送门


Problem

Alice 出了 n n n 场比赛,第 i i i 场比赛有 a i a_i ai 道题。

由于是提高组模拟赛,因此最多只会有三题

Bob 要来切这些题。由于他艺高人胆大,所以他的切题方式比较特别。

Bob 1 1 1 n n n n n n 个整数中等概率均匀随机生成一个数 i i i,作为他选择的比赛序号。如果第 i i i 场比赛还有 Bob 没切的题,Bob 会切掉一题,并说一声 “ “ 我好菜啊 ” ” ;否则他切不了题,但是由于他 AK 了点开的这场比赛,所以他同样会说 “ “ 我好菜啊 ” ”

每次进行完上述操作后,Bob 会从随机开始重新进行上述步骤,直到所有题被切完为止。

没多久,Bob 切完了所有的题,而 Alice 已经数不清楚他说了多少遍“我好菜啊”。

由于 Bob 的选择是随机的,可能无法还原真实情况,所以 Alice 只希望知道 Bob 期望说 “ “ 我好菜啊 ” ” 的次数。

期望就是指无数多次试验下的平均值。

也就是说,Alice 想知道,假设有无限次这样的试验,Bob 平均情况下经过几轮上述步骤能够最早切完所有题。

但是 Alice 不会概率期望,所以现在请你帮帮 Alice 吧。

由于 Bob 尤其喜欢切傅里叶变换的题,因此答案模 17680321 17680321 17680321(傅里叶的生日,是个质数)。

对于 100 % 100\% 100% 的数据, n ≤ 500 n≤500 n500


Solution

很容易看出这是一道期望概率 d p dp dp题吧。

由于每个比赛最多只有 3 3 3 道题,我们设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k] 表示还有 1 1 1 道题没做的比赛数为 i i i,还有 2 2 2 道题没做的比赛数为 j j j,还有 3 3 3 道题没做的比赛数为 k k k

那么状态转移方程就很明确了,即:

f [ i ] [ j ] [ k ] = i n f [ i − 1 ] [ j ] [ k ] + j n f [ i + 1 ] [ j − 1 ] [ k ] + k n f [ i ] [ j + 1 ] [ k − 1 ] + n − i − j − k n f [ i ] [ j ] [ k ] + 1 f[i][j][k]=\frac{i}{n}f[i-1][j][k]+\frac{j}{n}f[i+1][j-1][k]+\frac{k}{n}f[i][j+1][k-1]+\frac{n-i-j-k}{n}f[i][j][k]+1 f[i][j][k]=nif[i1][j][k]+njf[i+1][j1][k]+nkf[i][j+1][k1]+nnijkf[i][j][k]+1

把右边的 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k] 移到左边,再化一下简,得:

f [ i ] [ j ] [ k ] = i i + j + k f [ i − 1 ] [ j ] [ k ] + j i + j + k f [ i + 1 ] [ j − 1 ] [ k ] + k i + j + k f [ i ] [ j + 1 ] [ k − 1 ] + n i + j + k f[i][j][k]=\frac{i}{i+j+k}f[i-1][j][k]+\frac{j}{i+j+k}f[i+1][j-1][k]+\frac{k}{i+j+k}f[i][j+1][k-1]+\frac{n}{i+j+k} f[i][j][k]=i+j+kif[i1][j][k]+i+j+kjf[i+1][j1][k]+i+j+kkf[i][j+1][k1]+i+j+kn

然后按照这个式子转移就可以了。

有一个要注意的地方,即此题的空间限制较小, O ( n 3 ) O(n^3) O(n3) 的空间开不下,用滚动数组优化一维即可。


Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 505
#define Mod 17680321
using namespace std;
int cnt[5],inv[N],f[N][N][2];
int main(){
	int n,x,t=0;
	scanf("%d",&n);
	inv[1]=1;
	for(int i=2;i<=n;++i)  inv[i]=1ll*(Mod-Mod/i)*inv[Mod%i]%Mod;
	for(int i=1;i<=n;++i)  scanf("%d",&x),cnt[x]++;
	f[0][0][0]=0;
	for(int k=0;k<=n;++k){
		for(int j=0;j+k<=n;++j){
			for(int i=0;i+j+k<=n;++i){
				if(i||j||k){
					f[i][j][t]=1ll*n*inv[i+j+k]%Mod;
					f[i][j][t]=(f[i][j][t]+1ll*i*f[i-1][j][t]%Mod*inv[i+j+k]%Mod)%Mod;
					f[i][j][t]=(f[i][j][t]+1ll*j*f[i+1][j-1][t]%Mod*inv[i+j+k]%Mod)%Mod;
					f[i][j][t]=(f[i][j][t]+1ll*k*f[i][j+1][t^1]%Mod*inv[i+j+k]%Mod)%Mod;
					if(i==cnt[1]&&j==cnt[2]&&k==cnt[3])  return printf("%d",f[i][j][t]),0;
				}
			}
		}
		t^=1;
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值