【CF913F】Strongly Connected Tournament 概率神题

【CF913F】Strongly Connected Tournament

题意:有n个人进行如下锦标赛:

1.所有人都和所有其他的人进行一场比赛,其中标号为i的人打赢标号为j的人(i<j)的概率为$p=a\over b$。
2.经过过程1后我们相当于得到了一张竞赛图,将图中所有强联通分量缩到一起,可以得到一个链,然后对每个大小>1的强联通分量重复过程1。
3.当没有大小>1的强连通分量时锦标赛结束。

现在给出n,a,b,求期望比赛的场数。

$n\le 2000,a<b\le 1000$

题解:神题。先给出DP方程:

$ans(0)=ans(1)=0$

$ans(i)=\sum\limits_{j=1}^istrong(j)\times cp(i,j) \times (\frac {j(j-1)} 2+j(i-j)+ans(j)+ans(i-j)),i>1$

解释一下,我们此处相当于枚举了链上最后一个强联通分量的大小。其中strong(j)表示一个大小为j的点集是强连通分量的概率,cp(i,j)表示i中选出j个人使得这j个人全部输给了余下i-j个人的概率。注意这个方程的左右两边都有ans(i)这一项,我们到时候应该移项处理。

如何求strong(i)呢?我们考虑容斥,枚举其中出现一个大小<i的强联通分量的概率,即:

$strong(i)=1-\sum\limits_{j=1}^{i-1}strong(j)\times cp(i,j)$

如何求cp(i,j)呢?求法类似于组合数的递推方法。我们考虑i中标号最大的那个人,便能得到:

$cp(i,j)=cp(i-1,j-1)\times (1-p)^{i-j}+cp(i-1,j)\times p^j$

时间复杂度$O(n^2)$。

 

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;
typedef long long ll;
const ll P=998244353;
int n;
ll p;
ll c[2010][2010],s[2010],f[2010],p0[2010],p1[2010];
inline ll pm(ll x,ll y)
{
	ll z=1;
	while(y)
	{
		if(y&1)	z=z*x%P;
		x=x*x%P,y>>=1;
	}
	return z;
}
int main()
{
	scanf("%d",&n);
	int i,j,a,b;
	scanf("%d%d",&a,&b),p=a*pm(b,P-2)%P;
	f[0]=f[1]=0,s[1]=1;
	for(p0[0]=p1[0]=1,i=1;i<=n;i++)	p0[i]=p0[i-1]*p%P,p1[i]=p1[i-1]*(1-p)%P;
	for(i=0;i<=n;i++)
	{
		c[i][0]=1;
		for(j=1;j<=i;j++)	c[i][j]=(c[i-1][j]*p1[j]+c[i-1][j-1]*p0[i-j])%P;
	}
	for(i=1;i<=n;i++)
	{
		s[i]=1;
		for(j=1;j<i;j++)	s[i]=(s[i]-s[j]*c[i][j])%P;
	}
	for(i=2;i<=n;i++)
	{
		ll tmp=0;
		for(j=1;j<i;j++)	tmp=(tmp+s[j]*c[i][j]%P*(f[j]+f[i-j]+j*(j-1)/2+j*(i-j)))%P;
		tmp=(tmp+s[i]*(i*(i-1)/2))%P;
		f[i]=tmp*pm(1-s[i],P-2)%P;
	}
	printf("%lld",(f[n]+P)%P);
	return 0;
}

 

posted @ 2018-01-14 12:34  CQzhangyu  阅读(464)  评论(0编辑  收藏  举报