CF1479E School Clubs

(m)组,每组有(a_i)个人。(n=sum a_i)

每次等概率选出一个人,这个人有一半的概率从这组中脱离并创建一个组,有一半的概率从这组中脱离,并且以(frac{a_i}{n})的概率回到组(i)(可能回到原组)。问所有人都在同一组的概率。

(nle 4*10^8,mle 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 eq 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
",(sp*qpow(sq)-ans+mo)%mo);
	return 0;
}
原文地址:https://www.cnblogs.com/jz-597/p/14423340.html