[做题记录] ZJOI2019 开关 题解

ZJOI2019 开关

题意

\(n\) 个开关, 初始的时候都是 \(0\), 给出一个状态 \(s\) , 每次以 \(\frac{p_i}{\sum_{j = 1}^np_j}\) 的概率按下一个开关 \(i\) 期望多少次以后可以第一次达到这个状态。
\(n \leq 100, \sum p_i \leq 5\times 10^4\)

题解

\(a_i = \frac{p_i}{\sum_{i = 1}^np_j}, m = \sum_{i = 1}^n p_i\)
考虑生成一个操作序列, 讨论每个位置要按下的奇偶性, 可以直接写出操作序列出现概率的EGF。

\[\hat{F}(x) = \prod_{i = 1}^n \frac{e^{a_ix}+ (-1) ^{s_i}e^{-a_ix}}{2} \]

但是这个东西显然啥用也没有, 因为我们要求出的是第一次到达这个状态的EGF, 这个求出来的显然只是所有最终状态是 \(s\) 的EGF。

考虑一个经典的构造, 令第一次到达状态 \(s\) 的生成函数是 \(H(x)\), 若干次操作以后回到原状态的序列的生成函数是 \(\hat{G}(x)\) , 那么答案就是 \(H'(1)\)
不难写出 \(\hat{G}(x)\) 的表达式, 也就是 \(s_i\) 全部是 偶数的情况。

\[\hat{G}(x) = \prod_{i = 1}^n \frac{e^{a_ix}+ e^{-a_ix}}{2} \]

考虑一个组合意义, “到达 \(s\) 的概率 \(=\) 第一次到达 \(s\) 的概率, 经过若干次重新到达 \(s\) 的概率(可以是\(0\)次)”。
这显然只是一个卷积的意义, 可以得到方程 \(F = GH\) 。也就是 \(H = \frac{F}{G}\)
注意这里的 \(F, G\) 是上文 \(EGF\)\(OGF\) 形式。

简单求导可以得到 \(H'(1) = \frac{F'(1)G(1) - F(1)G'(1)}{G^2(1)}\)。现在问题变成对 \(F', G', F, G\) 求系数和。

\(F\) 为例考虑对其动手动脚。

\[\hat{F} = \sum_jf_je^{\frac{j}{m}x} \]

那么

\[F = \sum_jf_j\frac{m}{m - jx} \]

这一步操作仅仅是转了一次OGF。
然后现在考虑求一波系数, 但是发现有点问题在于 \(F\)\(j = m, x = 1\) 的时候没意义了。
然后你点开了题解发现大家都乘了一个 \(1 - x\)
考虑把 \(F\)\(G\) 同时乘一个 \(1 - x\) 答案是不变的, 而且发现此时函数 \(F, G\) 均收敛。
然后再考虑计算其系数。
\(P(x) = F(x)(1 - x), Q(x) = G(x)(1 - x)\)
通过求导容易知道 \(P(1) = f_m, P'(1) = \sum_{-m \leq j \leq m - 1}f_j\frac{m}{j - m}\)
\(Q, Q'\) 可以同理得出。
直接大力背包出 \(f_i, g_i\) 就可以暴算了。

#include <bits/stdc++.h>

const int P = 998244353;
inline int mod(int x) { return x + (x >> 31 & P); }
inline void pls(int &x, int y) { x = mod(x + y - P); }
inline void sub(int &x, int y) { x = mod(x - y); }
inline int power(int x, int k) {
	int res = 1; if(x < 0) x += P;
	while(k) {
		if(k & 1) res = 1ll * res * x % P;
		x = 1ll * x * x % P; k >>= 1;
	}
	return res;
}

const int N = 100 + 5, M = 5e4 + 5;

int n, s[N], p[N], m;
int pool[3][M << 1];
int *F = pool[0] + M, *G = pool[1] + M, *t = pool[2] + M;

int main() {
	std :: ios :: sync_with_stdio(false);
	std :: cin.tie(0); std :: cout.tie(0);
	std :: cin >> n;
	for(int i = 1; i <= n; i ++) std :: cin >> s[i];
	for(int i = 1; i <= n; i ++) std :: cin >> p[i], m += p[i];
	F[0] = 1;
	for(int i = 1; i <= n; i ++) {
		int sgn = (s[i] == 1 ? P - 1 : 1);
		for(int j = - m; j <= m; j ++)
			if(F[j]) 
				pls(t[j + p[i]], F[j]), pls(t[j - p[i]], 1ll * F[j] * sgn % P);
		for(int j = - m; j <= m; j ++) F[j] = t[j], t[j] = 0;
	} 
	G[0] = 1;
	for(int i = 1; i <= n; i ++) {
		for(int j = - m; j <= m; j ++)
			if(G[j]) pls(t[j + p[i]], G[j]), pls(t[j - p[i]], G[j]);
		for(int j = - m; j <= m; j ++) G[j] = t[j], t[j] = 0;
	}
	int p1 = F[m], p2 = 0, q1 = G[m], q2 = 0;
	for(int i = - m; i <= m - 1; i ++)
		pls(p2, 1ll * F[i] * m % P * power(i - m, P - 2) % P);
	for(int i = - m; i <= m - 1; i ++)
		pls(q2, 1ll * G[i] * m % P * power(i - m, P - 2) % P);
	int ans = 1ll * (1ll * p2 * q1 % P - 1ll * p1 * q2 % P + P) * power(1ll * q1 * q1 % P, P - 2) % P;
	std :: cout << ans << std :: endl; 	
	return 0;
}
posted @ 2022-01-23 23:58  HN-wrp  阅读(131)  评论(0编辑  收藏  举报