1. 程式人生 > 其它 >題解 LOJ #3045. 「ZJOI2019」開關

題解 LOJ #3045. 「ZJOI2019」開關

組合數學,多項式,生成函式。

題目連結

方便起見,先令所有 \(p_i\leftarrow \frac{p_i}{\sum p}\)

一上來會想到的應該是如下的兩個柿子:

\[\left\{ \begin{aligned} \sinh(x)&=x+\frac{x^3}{3!}+\frac{x^5}{5!}+\cdots=\frac{e^x-e^{-x}}{2}\\ \cosh(x)&=1+\frac{x^2}{2!}+\frac{x^4}{4!}+\cdots=\frac{e^x-e^{x}}{2}\\ \end{aligned} \right. \]

然後考慮設一個 \(f_n\),表示經過 \(n\) 次之後變得合法的概率。不難得到:

\[f_n=n![x^n]\prod_{i}\frac{e^{p_ix}+(-1)^{s_i}e^{-p_ix}}{2} \]

但是我們要求的是第一次合法的期望步數,於是再搞一個 \(g_n\) 表示經過 \(n\) 次之後不變的概率,這就是所有 \(s_i\) 全部等於 \(0\) 的情況,亦即

\[g_n=n![x^n]\prod_{i}\frac{e^{p_ix}+e^{-p_ix}}{2} \]

考慮設 \(F,G\) 分別為其對應的 \(\rm OGF\),不難得到最後的答案就是

\[\frac{\mathrm d}{\mathrm dx}\frac{F(x)}{G(x)}|_{x=1} \]

考慮我們其實是要把一個 \(\rm EGF\)

變成 \(\rm OGF\),這啟發我們使用組合 Laplace 變換

\[\int_0^\infty F(xt)e^{-t}\mathrm dt=\sum_{i\geq 0}x^ii![x^i]F(x) \]

於是考慮把那個很醜陋的 \(\sinh\)\(\cosh\) 通過揹包化簡為如下的形式:

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

這個形式是比較簡潔的,直接帶入變換即有:

\[\begin{aligned} F(x)&=\int_0^\infty \hat F(xt)e^{-t}\mathrm dt\\ &=\int_0^\infty \sum_i c_ie^{-(1-ix)t}\mathrm dt\\ &=\sum_i c_i\int_0^\infty e^{-(1-ix)t}\mathrm dt\\ &=\sum_i \frac{c_i}{1-ix}\\ \end{aligned} \]

考慮當 \(i=1\)

的時候這個東西沒有定義,我們給 \(F(x)\)\(G(x)\) 上下同時乘上一個 \(1-x\),然後大力化簡即有:

\[\left\{ \begin{aligned} F(1)&=c_{1}\\ F'(1)&=\sum_{i\neq 1}\frac{c_i}{ix-1} \end{aligned} \right. \]

揹包預處理係數,然後大力計算即可。複雜度 \(\mathcal O(n\sum p)\)

#include<cstdio>
#include<cstring>
#include<algorithm>

typedef long long ll;
const ll mod = 998244353;
const ll inv2 = (mod + 1) / 2;

const int maxn = 105;
const int maxm = 5E+4 + 5;

inline ll fsp(ll a, ll b, ll res = 1) {
	for(a %= mod; b; a = a * a % mod, b >>= 1)
		b & 1 && (res = res * a % mod); return res;
}
inline ll sgn(int x) { return x & 1 ? -1 : 1; }

int n, m, s[maxn], p[maxn];
ll dp[maxn][maxm << 1];
inline std::pair<ll, ll> getval() {
	memset(dp, 0, sizeof dp);
	
	dp[0][m] = 1;
	for(int i = 1; i <= n; ++i) {
		for(int j = -m; j <= m; ++j) {
			if(j - p[i] >= -m) (dp[i][j + m] += inv2 * dp[i - 1][j - p[i] + m]) %= mod;
			if(j + p[i] <= m) (dp[i][j + m] += sgn(s[i]) * inv2 * dp[i - 1][j + p[i] + m]) %= mod;
		}
	}
	
	ll f = dp[n][m + m], df = 0, inv = fsp(m, mod - 2);
	for(int i = -m; i < m; ++i)
		(df += fsp(i * inv - 1, mod - 2, dp[n][i + m])) %= mod;
	return std::make_pair(f, df);
}

int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i) scanf("%d", &s[i]);
	for(int i = 1; i <= n; ++i) scanf("%d", &p[i]), m += p[i];
	
	auto f = getval();
	for(int i = 1; i <= n; ++i) s[i] = 0;
	auto g = getval();
	
	ll ans = (f.second * g.first - f.first * g.second) % mod;
	ans = fsp(g.first * g.first, mod - 2, ans) % mod;
	printf("%lld\n", (ans + mod) % mod);
}