CF913F

CF913F

给定 \(n\)\(p\),现在有一张 \(n\) 个点的完全无向图。并按照此规则:

  1. 先给每条边随机定向,以 \(p\) 的概率从编号小的指向大的,以 \(1-p\) 的概率从大的指向小的,此时的贡献为被定向的边的边数。
  2. 然后缩点,这样会剩余一张形如 \(A_1\to A_2\to A_3\to ...\to A_k\) 的图。
  3. 对于每个 \(i=1,2...k\) 如果 \(|A_i|\ne 1\),那么对这个连通块的子图重新执行这个流程。

求期望贡献和。

\(n\le 2000\)

Solution

注意到点是等价的,同时递归处理的时候存在很明显的子问题性,那么考虑递推,设 \(f_i\) 表示 \(i\) 个点的时候的答案,那么 \(f_1=0\)

枚举 Top 序最高的连通块的大小 \(j\),那么设 \(g_j\) 表示 \(j\) 个点的完全图生成的所有强连通图的概率和。

此时贡献为:

\[f_i=\binom{i}{2}+\sum_{j} g_j\times (j\to other)\times (f_j+h_{i-j}) \]

其中 \(h\) 表示大小为 \(i-j\) 的图,已经定了边的期望次数(等价于拿掉了 \(\binom{i}{2}\))后的式子。

考虑 \(j\to other\) 如何计算,不难发现我们只关注那些点被选入了 Top 序最高的强连通块中。

假设被选入的点集为 \(S\),此时贡献为 \(\prod_{x\in S} (\prod_{i\not \in S,i<x}p\prod_{i\not \in S,i>x}(1-p))\),设为 \(w(S)\),我们想求的即 \(\sum_{|S|=j} w(S)\)

\(\sum_{|S|=j}w(S)\) 的处理可以通过 dp 处理,设 \(dp_{i,j}\) 表示当前考虑到编号 \(i\),当前的集合 \(S\) 大小为 \(j\),那么考虑加入一个 \(i+1\),此时转移为:

  1. \(dp_{i+1,j}\leftarrow dp_{i,j}\times p^j\)
  2. \(dp_{i+1,j+1}\leftarrow dp_{i,j}\times (1-p)^{i-j}\)

我们可以 \(\mathcal O(n^2)\) 的预处理 \(f\) 数组。

考虑大小为 \(j\) 的完全图生成的强连通图的概率和 \((g_j)\)

我们考虑计算所有可能的图减去非强连通图的方案数。

我们考虑枚举 Top 最高的点的数量 \(k\),此时对答案的贡献为 \(-(k\to other)\times g_{k}\)

不难发现此处需要用到的 \((k\to other)\) 和之前要用的类似,仅为 \(dp\) 数组在 \(i\) 较小的情况!(即点数不为 \(n\) 的情况)

那么我们可以 \(\mathcal O(n^2)\) 的递推 \(g_k\),然后可以 \(\mathcal O(n^2)\) 的递推 \(f\)\(h\) 即可。

\(Code:\)

#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
#define int long long 
int gi() {
	char cc = getchar() ; int cn = 0, flus = 1 ;
	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
	return cn * flus ;
}
const int N = 3000 + 5 ; 
const int P = 998244353 ; 
int fpow(int x, int k) {
	int ans = 1, base = x ;
	while(k) {
		if(k & 1) ans = 1ll * ans * base % P ;
		base = 1ll * base * base % P, k >>= 1 ;
	} return ans ;
}
int n, p, fv1[N], fv2[N], dp[N][N], h[N], g[N], f[N] ; 
void inc(int &x, int y) { x += y, x %= P ; }
signed main()
{
	n = gi() ; int a = gi(), b = gi() ;
	p = a * fpow(b, P - 2) % P, fv1[0] = fv2[0] = 1 ;
	rep( i, 1, n ) fv1[i] = fv1[i - 1] * p % P ;
	rep( i, 1, n ) fv2[i] = fv2[i - 1] * (1 - p + P) % P ; 
	dp[0][0] = 1 ;
	for(int i = 0; i < n; ++ i) {
		rep( j, 0, i ) 
			inc( dp[i + 1][j], dp[i][j] * fv1[j] % P ),
			inc( dp[i + 1][j + 1], dp[i][j] * fv2[i - j] % P ) ;
	}
	g[1] = 1, h[1] = f[1] = 0 ; 
	rep( i, 2, n ) {
		g[i] = 1 ; 
		for(re int k = 1; k < i; ++ k) 
			inc(g[i], P - g[k] * dp[i][k] % P) ; 
		int k = 1 - g[i] + P, c = (i * (i - 1) / 2) ; 
		k %= P, k = fpow(k, P - 2) ; 
		for(re int j = 1; j < i; ++ j)
		inc(c, g[j] * dp[i][j] % P * (f[j] + h[i - j]) % P) ; 
		f[i] = c * k % P ;
		for(re int j = 1; j <= i; ++ j)
		inc(h[i], g[j] * dp[i][j] % P * (f[j] + h[i - j]) % P) ; 
	}
	printf("%lld\n", f[n] ) ; 
	return 0 ;
}
posted @ 2020-09-24 16:13  Soulist  阅读(254)  评论(0编辑  收藏  举报