1. 程式人生 > >【CF913F】Strongly Connected Tournament 概率神題

【CF913F】Strongly Connected Tournament 概率神題

左右 sca str names a* int tmp 一場 過程

【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;
}

【CF913F】Strongly Connected Tournament 概率神題