CF1479E School Clubs
有\(m\)组,每组有\(a_i\)个人。\(n=\sum a_i\)。
每次等概率选出一个人,这个人有一半的概率从这组中脱离并创建一个组,有一半的概率从这组中脱离,并且以\(\frac{a_i}{n}\)的概率回到组\(i\)(可能回到原组)。问所有人都在同一组的概率。
\(n\le 4*10^8,m\le 1500\)
和这题一个套路:https://www.cnblogs.com/jz-597/p/13775587.html
理论基础:https://www.cnblogs.com/p-b-p-b/p/13297098.html
现在要找出这个势函数。对于某个局面,猜想势函数\(\phi=\sum g(a_i)\)。显然\(g(0)=0\)。
强行列出式子:
\[\phi=1+\sum_i \frac{a_i}{n}(\frac{1}{2}(\phi-g(a_i)+g(a_i-1)+g(1))+\frac{1}{2}(\frac{a_i}{n}\phi+\sum_{j\neq i}\frac{a_j}{n}(\phi-g(a_i)+g(a_i-1)-g(a_j)+g(a_j+1))))
\]
化简得:
\[-2n-ng(1)=\sum_i a_i((2-\frac{a_i}{n})g(a_i-1)-(3-\frac{a_i}{n})g(a_i)+(1-\frac{a_i}{n})g(a_i+1))
\]
记后面这坨东西为\(h(a_i)\)。
我们希望\(\sum h(a_i)\)和\(a_i\)具体的取值无关,如果取出某个\(a_i\),把它拆成两个部分再放进去,结果是一样的。也就是说\(h(x+y)=h(x)+h(y)\)。所以不妨假设\(\frac{h(x)}{x}\)相等。
所以:
\[-2-g(1)=(2-\frac{x}{n})g(x-1)-(3-\frac{x}{n})g(x)+(1-\frac{x}{n})g(x+1)
\]
记\(f(i)=g(i)-g(i-1)\)。上式变为:
\[-2-g(1)=(1-\frac{x}{n})f(x+1)-(2-\frac{x}{n})f(x)
\]
取\(g(1)=-2\)。于是得到\(f(x)=-\prod_{i=0}^{x-1}\frac{2n-i}{n-i}\)。
于是可以在\(O(n)\)时间内处理出所有\(g(x)\)(维护分数)。
直接\(O(n)\)做可以过(CF上开64位编译器)。
std似乎是用了类似快速阶乘的方法。
using namespace std;
#include <bits/stdc++.h>
#define N 1505
#define ll long long
#define mo 998244353
int m;
int a[N],n;
ll qpow(ll x,ll y=mo-2){
ll r=1;
for (;y;y>>=1,x=x*x%mo)
if (y&1)
r=r*x%mo;
return r;
}
int main(){
freopen("in.txt","r",stdin);
scanf("%d",&m);
for (int i=1;i<=m;++i)
scanf("%d",&a[i]),n+=a[i];
sort(a+1,a+m+1);
ll ans=0;
register ll sp=0,sq=1,tp=1,tq=1;
for (register int i=0,j=1;i<n;++i){
tp=tp*((n<<1)-i)%mo;
tq=tq*(n-i)%mo;
sp=(sp*tq+sq*tp)%mo;
sq=sq*tq%mo;
for (;j<=m && a[j]==i+1;++j)
(ans+=sp*qpow(sq))%=mo;
}
printf("%lld\n",(sp*qpow(sq)-ans+mo)%mo);
return 0;
}