其他分享
首页 > 其他分享> > 「题解」老头子的话

「题解」老头子的话

作者:互联网

本文将同步发布于:

题目

题目链接:「集训队作业 2018」喂鸽子

题目描述

老头子是小学校长,小学生(大哥)们都很听老头子的话。一天,老头子给小学生(大哥)们发苹果吃。一共有 \(n\) 个小学生(大哥),老头子每一次会等概率选择一位小学生(大哥)并给他一个苹果。一个小学生(大哥)变得开心当且仅当他拥有的苹果数 \(\geq k\)。

因为老头子年纪大了,所以他想要你告诉他,期望多少次之后所有的小学生(大哥)都变得开心。

\(n\leq 50\),\(k\leq 10^3\)。

题解

简单的 min-max 容斥

设操作序列 \(s\),设第 \(i\) 个人对应的第 \(j\) 次操作为 \(a_{i,j}\),那么

我们求解的是 \(\mathbb{E}(\max\limits_{i=1}^n\{a_i\})\)。

我们不难发现,套用 min-max 容斥的分析方法,有:

\[\mathbb{E}(\max\limits_{i=1}^n\{a_{i,k}\})=\sum_{\mathbb{S}}(-1)^{\left\lvert\mathbb{S}\right\rvert+1}\mathbb{E}(\min_{i=1}^n\{a_{i,k}\}) \]

考虑枚举 \(\min\limits_{i=1}^n\{a_{i,k}\}\),那么我们可以通过组合数推出对应的序列个数,从而计算出 \(\mathbb{E}(\min\limits_{i=1}^n\{a_{i,k}\})\)。

用 NTT 优化可以使得复杂度达到 \(\Theta(nk\log_2k)\)。

巧妙构造

上面的做法看上去还是太难了,我们不难想到更加简单的做法。

考虑“有实质变化”的玉米,即喂给了一个没有饱的鸽子的玉米

还是考虑每个“有效玉米序列”的贡献,就是出现概率 \(\times\) 期望

一个固定的“有效玉米序列”,出现概率和期望都和每次扔玉米时已经饱的鸽子有关系

所以状态多记录上饱的鸽子数量。

至于怎样判断一个鸽子饱了,先填“白色”有效玉米,

想让一个鸽子饱了,就钦定之前 \(k-1\) 个白玉米染上色!

所以这个白玉米还是“对未来承诺”,或者对未来预留的 trick。

状态保留贡献和和概率和即可。是可以转移的。

参考程序

#include<bits/stdc++.h>
using namespace std;
#define reg register
typedef long long ll;

const int MAXN=50+5;
const int MAXK=1e3+5;
const int mod=998244353;

inline int add(reg int a,reg int b){
	a+=b;
	return a>=mod?a-mod:a;
}

inline int sub(reg int a,reg int b){
	a-=b;
	return a<0?a+mod:a;
}

inline int fpow(reg int x,reg int exp){
	reg int res=1;
	while(exp){
		if(exp&1)
			res=1ll*res*x%mod;
		x=1ll*x*x%mod;
		exp>>=1;
	}
	return res;
}

int n,k;
int fac[MAXN*MAXK],invfac[MAXN*MAXK],inv[MAXN*MAXK];

inline int binom(reg int n,reg int m){
	if(m<0||n<m)
		return 0;
	else
		return 1ll*fac[n]*invfac[m]%mod*invfac[n-m]%mod;
}

int f[MAXN*MAXK][MAXN],g[MAXN*MAXK][MAXN];

int main(void){
	scanf("%d%d",&n,&k);
	fac[0]=1;
	for(reg int i=1;i<=n*k;++i)
		fac[i]=1ll*fac[i-1]*i%mod;
	invfac[n*k]=fpow(fac[n*k],mod-2);
	for(reg int i=n*k-1;i>=0;--i)
		invfac[i]=1ll*invfac[i+1]*(i+1)%mod;
	for(reg int i=1;i<=n*k;++i)
		inv[i]=1ll*invfac[i]*fac[i-1]%mod;
	f[0][0]=0,g[0][0]=1;
	for(reg int i=0;i<n*k;++i)
		for(reg int j=0;j*k<=i;++j){
			reg int tmp1=add(1ll*inv[n-j]*f[i][j]%mod,1ll*inv[n-j]*(1ll*n*inv[n-j]%mod)%mod*g[i][j]%mod),tmp2=1ll*inv[n-j]*g[i][j]%mod;
			if(j*k<=(i+1))
				f[i+1][j]=add(f[i+1][j],tmp1),g[i+1][j]=add(g[i+1][j],tmp2);
			if((j+1)*k<=(i+1))
				f[i+1][j+1]=add(f[i+1][j+1],1ll*binom(i-j*k,k-1)*tmp1%mod),g[i+1][j+1]=add(g[i+1][j+1],1ll*binom(i-j*k,k-1)*tmp2%mod);
		}
	reg int ans=1ll*fac[n]*f[n*k][n]%mod;
	printf("%d\n",ans);
	return 0;
}

标签:mathbb,鸽子,min,int,题解,小学生,的话,老头子,reg
来源: https://www.cnblogs.com/Lu-Anlai/p/14969801.html