【CF913F】Strongly Connected Tournament
题意:有n个人进行如下锦标赛:
1.所有人都和所有其他的人进行一场比赛,其中标号为i的人打赢标号为j的人(i<j)的概率为$p=aover b$。
2.经过过程1后我们相当于得到了一张竞赛图,将图中所有强联通分量缩到一起,可以得到一个链,然后对每个大小>1的强联通分量重复过程1。
3.当没有大小>1的强连通分量时锦标赛结束。
现在给出n,a,b,求期望比赛的场数。
$nle 2000,a<ble 1000$
题解:神题。先给出DP方程:
$ans(0)=ans(1)=0$
$ans(i)=sumlimits_{j=1}^istrong(j) imes cp(i,j) imes (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-sumlimits_{j=1}^{i-1}strong(j) imes cp(i,j)$
如何求cp(i,j)呢?求法类似于组合数的递推方法。我们考虑i中标号最大的那个人,便能得到:
$cp(i,j)=cp(i-1,j-1) imes (1-p)^{i-j}+cp(i-1,j) imes 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; }