[JZOJ6340] 【NOIP2019模拟2019.9.4】B

题目

题目大意

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


思考历程

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


正解

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


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#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
",sum%mo);
	return 0;
}

总结

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

原文地址:https://www.cnblogs.com/jz-597/p/11483219.html