杂题集萃[5]

题意

给出一个长度为 (N) 的数列 (a)

进行若干轮操作,每次操作在 ([1,N]) 中等概率选择一个使得 (a_i>0)(i),并将 (a_i) 减少 (1)

问,期望多少次操作后 (a_1) 被减成了零。

输入格式

第一行一个整数 (N)

第二行 (N) 个整数,第 (i) 个为 (a_i)

输出格式

一行一个整数,表示答案。为避免精度误差,答案对 323232323取模。

即设答案化为最简分式后的形式为 (frac{a}{b}),其中 (a)(b) 互质。

输出整数 (x) 使得 (bx≡a(mod323232323))(0≤x<323232323)

可以证明这样的整数 (x) 是唯一的。

input

3

2 3 3

output

202020207

限制与约定

题解

算法 1

答案可以看成是每一个元素被选中的次数之和。由于期望的线性性,我们可以去计算每一个位置被选中的次数的期望。

首先,第一个元素一定被减了 (a_1) 次。

考虑某一个位置 (i),假设当前有 (c) 个元素不为 (0),那么每个元素被操作的概率都是 (frac{1}{c})

倘若只关注 (1)(i) 两个元素,可以发现操作其它元素的时候对它们没有影响,而且它们两个被操作的概率是相等的。

于是这个问题就等价与一个只有两个元素的原问题。

因此元素之间是独立的!使用算法 1 中的动态规划就可以知道每个元素对答案的贡献,求和即可。

时间复杂度 (O(a^2+n))。期望得分 (60) 分。

算法 2

算法 3 中的动态规划可以看成从 ((a_1, a_i)) 出发的随机游走,每次随机一个方向将减 (1),直到走到坐标轴上为止。

若停在 ((0,a)),对答案的贡献为 (a_i-a)。若停在 ((a,0)),对答案的贡献为 (a_i)

于是可以直接写出贡献的式子。

[sum_{i=0}^{a_i-1}i*frac{a_1-1+ichoose i}{2^{a_1+i}}+a_i(1-sum_{i=0}^{a_i-1}frac{a_1-1+ichoose i}{2^{a_1+i}}) ]

前面那项是停留在 ((0,a)) 的答案,后面那项是停留在 ((a,0)) 的答案。

(a_i) 增加 (1) 的时候,变化的贡献可以在 (O(1)) 的时间内得到。(前后都是只增加了一项)

时间复杂度 (O(a+n))。期望得分 (100) 分。

code

#include<bits/stdc++.h>
#define int long long
#define re register int
using namespace std;
inline void read(int &x){
	x=0;char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(; isdigit(ch);ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
}
const int N=1000010,i2=161616162,P=323232323;
int n,m,s,p,a[N],fac[N],inv[N],f[N];
inline int pw(int a,int b){
	int res=1;
	for(;b;b>>=1,a=a*a%P)
		if(b&1)res=res*a%P;
	return res;
}
inline int C(int a,int b){
	return b<0||b>a?0:fac[a]*inv[b]%P*inv[a-b]%P;
}
signed main(){
	read(n);
	for(re i=1;i<=n;i++)read(a[i]),s+=a[i],s%=P;
	for(re i=fac[0]=1;i<N;i++)fac[i]=fac[i-1]*i%P;
	inv[N-1]=pw(fac[N-1],P-2);
	for(re i=N-2;i>=0;i--)inv[i]=inv[i+1]*(i+1)%P;
	f[1]=p=pw(2,P-1-a[1]);
	for(re i=2;i<N/2;i++)
		(p*=i2)%=P,f[i]=(f[i-1]*2-f[i-2]+C(a[1]+i-2,i-1)*p+P)%P;
	for(re i=2;i<=n;i++)s+=P-f[a[i]],s%=P;
	printf("%lld
",s);
	return 0; 
}
原文地址:https://www.cnblogs.com/Sparks-Pion/p/9656336.html