杂题集萃[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\)。
于是可以直接写出贡献的式子。
前面那项是停留在 \((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\n",s);
return 0;
}