• [做题记录] ZJOI2019 开关 题解 HN


    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;
    }
    
  • 相关阅读:
    git教程学习笔记(1)
    一句话懂什么是JS闭包
    attachEvent和addEventListener 的使用方法和区别
    地址栏中多个问号如何处理
    事件委托用法
    rem和em的区别
    echarts事件中获取当前实例
    this经典试题
    获取浏览器选中文本并操作
    android Activity launch mode 一个实例 singleInstance
  • 原文地址:https://www.cnblogs.com/clover4/p/15837930.html
Copyright © 2020-2023  润新知