[JZOJ6340] 【NOIP2019模拟2019.9.4】B

题目

题目大意

给你个非负整数数列 a a a,每次等概率选择大于零的 a i a_i ai,使其减 1 1 1
a 1 a_1 a1被减到 0 0 0的时候期望经过多少次操作。


思考历程

对于这题的暴力做法,显然可以状态压缩吧……
然后我突然意识到,实际上我们将题目转化成以下模型:
n n n种颜色,第 i i i种颜色的小球有 a i a_i ai个。那么题目就变成了一个有重复元素的排列问题。
先将 2 2 2 n n n排列求出来,然后考虑将 1 1 1随机插入。
枚举最后一个 1 1 1出现的位置,然后在前面用组合数计算插入的方案数。
这个做法打出来之后一直没有调出来,也不知道是我数学功底不好还是这个方法本来就是错的……
当然,即便是对的,也不会是满分做法。


正解

根据期望的线性性,题目可以转化为:对于每个 i > 1 i>1 i>1 a 1 a_1 a1减到 0 0 0的时候 a i a_i ai期望取了多少个。然后将每个 i i i的这东西加起来,再加上 a 1 a_1 a1,就是答案。
于是我们就只需要考虑这个问题。
接着就是最骚的操作:我们只考虑 a 1 a_1 a1 a i a_i ai,其它被减掉对它们没有影响。它们有相等的概率被减 1 1 1,这个概率可以看做是 1 2 \frac{1}{2} 21。于是相当于题目中 n = 2 n=2 n=2的情况。
现在考虑一个平面,一开始的坐标为 ( a 1 , a i ) (a_1,a_i) (a1,ai),每次随机地向下面或者左边走一格。
如果到达边界 ( 0 , y ) (0,y) (0,y),则会有 a i − y a_i-y aiy的贡献;如果到达边界 ( x , 0 ) (x,0) (x,0),则会有 a i a_i ai的贡献。
对于边界 ( 0 , y ) (0,y) (0,y),设向下走了 j j j步,则概率为 C a 1 + j − 1 j 2 a 1 + j \frac{C_{a_1+j-1}^{j}}{2^{a_1+j}} 2a1+jCa1+j1j
对于边界 ( x , 0 ) (x,0) (x,0),既然它们的贡献都一样,不妨将上面的和用 1 1 1减掉,就是它的概率。
接着就可以列出一个式子……
列出之后容易发现, ( a 1 , a i + 1 ) (a_1,a_i+1) (a1,ai+1)的答案可以 O ( 1 ) O(1) O(1)地从 ( a i , a i ) (a_i,a_i) (ai,ai)转移过来。


代码

using namespace std;
#include 
#include 
#include 
#include 
#define N 500010
#define A 500010
#define mo 323232323
inline int input(){
	char ch=getchar();
	while (ch<'0' || '9'<ch)
		ch=getchar();
	int x=0;
	do{
		x=x*10+ch-'0';
		ch=getchar();
	}
	while ('0'<=ch && ch<='9');
	return x;
}
int n,a[N];
int fac[N+A],invf[N+A],inv2[N+A];
int ans1[A*3],ans2[A*3],ans[A*3];
inline int inv(int x){
	int res=1;
	for (int y=mo-2;y;y>>=1,x=(long long)x*x%mo)
		if (y&1)
			res=(long long)res*x%mo;
	return res;
}
inline int C(int m,int n){return (long long)fac[m]*invf[n]%mo*invf[m-n]%mo;}
int main(){
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	n=input();
	for (int i=1;i<=n;++i)
		a[i]=input();
	fac[0]=1,inv2[0]=1;
	for (int i=1;i<=A*2;++i){
		fac[i]=(long long)fac[i-1]*i%mo;
		inv2[i]=(long long)inv2[i-1]*2%mo;
	}
	for (int i=0;i<=A*2;++i){
		invf[i]=inv(fac[i]);
		inv2[i]=inv(inv2[i]);
	}
	ans1[0]=ans2[0]=0;
	for (int i=1;i<=A;++i){
		ans1[i]=(ans1[i-1]+(long long)C(a[1]+i-2,i-1)*(i-1)%mo*inv2[a[1]+i-1]%mo)%mo;
		ans2[i]=(ans2[i-1]+(long long)C(a[1]+i-2,i-1)*inv2[a[1]+i-1]%mo)%mo;
		ans[i]=(ans1[i]+(long long)i*(1-ans2[i]+mo)%mo)%mo;
	}
	long long sum=a[1];
	for (int i=2;i<=n;++i)
		sum+=ans[a[i]];
	printf("%lld\n",sum%mo);
	return 0;
}

总结

期望的线性性真是奇妙啊……

你可能感兴趣的:(期望)