题意
给出一个长度为 (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)。
于是可以直接写出贡献的式子。
前面那项是停留在 ((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;
}