CF913F Strongly Connected Tournament 容斥+dp

版权声明:转吧转吧这条东西只是来搞笑的。。 https://blog.csdn.net/jpwang8/article/details/88976457

Description


n<=2000个人参加比赛

  1. 两两比一场,比完连个图,边i->j表示i赢了j
  2. 连完那个图强联通分量缩起来,强连通分量内继续比,即强连通分量递归进行1、2,直到每个强连通分量大小为1
  3. i<j时i有a/b的概率赢j,问每个人比赛的场数的总和的期望,答案%998244353。

n 2000 n\le2000

Solution


考虑设f[n]表示n个点的答案,一个套路就是枚举哪些点打成了一个强连通分量。设g[i]表示i个点打成一个强连通分量的概率,h[i,j]表示i个点里面j个点和剩余(i-j)个点打全输的概率,那么
f [ i ] = j = 1 i g [ j ] h [ i , j ] ( f [ i j ] + f [ j ] + ( j 2 ) + ( i j ) j ) f[i]=\sum_{j=1}^{i}{g[j]h[i,j]\left(f[i-j]+f[j]+\binom{j}{2}+\left(i-j\right)j\right)}
好像挺显然的吧。。
然后考虑g和h怎么求,g可以用1减去不合法的概率,那么就是 g [ i ] = 1 j = 1 i 1 g [ j ] h [ i , j ] g[i]=1-\sum_{j=1}^{i-1}{g[j]*h[i,j]}
一个图不强连通的话我们枚举一个出度为0的连通分量,然后让其余的边都指向它就行了

再看看h怎么求,我们可以讨论一下第i个人放在两个集合中的哪一个,于是就有 h [ i , j ] = h [ i 1 , j ] ( 1 p ) j + h [ i 1 , j 1 ] p i j h[i,j]=h[i-1,j]\cdot{(1-p)}^j+h[i-1,j-1]\cdot{p^{i-j}}
这里相当于我们默认i个中j个的编号比较大。。

然后我们发现f会转移到自己,那么移项解一个方程就完事了

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (LL i=st;i<=ed;++i)

typedef long long LL;
const int MOD=998244353;
const int ny2=(MOD+2)/2;
const int N=2005;

LL f[N],g[N],h[N][N];

LL ksm(LL x,LL dep) {
	LL res=1; x=(x%MOD+MOD)%MOD;
	for (;dep;dep>>=1) {
		(dep&1)?(res=res*x%MOD):0;
		x=x*x%MOD;
	}
	return res;
}

int main(void) {
	int n,a,b; scanf("%d%d%d",&n,&a,&b);
	LL p=a*ksm(b,MOD-2)%MOD;
	h[1][0]=h[1][1]=1;
	rep(i,2,n) {
		h[i][0]=1;
		rep(j,1,i) {
			h[i][j]=(h[i-1][j]*ksm(1+MOD-p,j)%MOD+h[i-1][j-1]*ksm(p,i-j)%MOD)%MOD;
		}
	}
	g[1]=1;
	rep(i,2,n) {
		rep(j,1,i-1) g[i]=(g[i]+g[j]*h[i][j]%MOD)%MOD;
		g[i]=(MOD+1-g[i])%MOD;
	}
	f[1]=0;
	rep(i,2,n) {
		f[i]=g[i]*h[i][i]%MOD*i%MOD*(i-1)%MOD*ny2%MOD;
		rep(j,1,i-1) {
			f[i]=(f[i]+(j*(j-1)%MOD*ny2%MOD+(i-j)*j%MOD+f[j]+f[i-j])%MOD*g[j]%MOD*h[i][j]%MOD)%MOD;
		}
		f[i]=f[i]*ksm(1-g[i]*h[i][i]%MOD,MOD-2)%MOD;
	}
	printf("%lld\n", f[n]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/88976457
今日推荐