zoukankan      html  css  js  c++  java
  • 题解 LOJ #3045. 「ZJOI2019」开关

    题目链接

    方便起见,先令所有 (p_ileftarrow frac{p_i}{sum p})

    一上来会想到的应该是如下的两个柿子:

    [left{ egin{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} ight. ]

    然后考虑设一个 (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) 分别为其对应的 ( m OGF),不难得到最后的答案就是

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

    考虑我们其实是要把一个 ( m EGF) 变成 ( m OGF),这启发我们使用组合 Laplace 变换

    [int_0^infty F(xt)e^{-t}mathrm dt=sum_{igeq 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} ]

    这个形式是比较简洁的,直接带入变换即有:

    [egin{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_iint_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{ egin{aligned} F(1)&=c_{1}\ F'(1)&=sum_{i eq 1}frac{c_i}{ix-1} end{aligned} ight. ]

    背包预处理系数,然后大力计算即可。复杂度 (mathcal O(nsum 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
    ", (ans + mod) % mod);
    }
    
  • 相关阅读:
    保留字段数组,一定要用char
    VirtualBox安装CentOS 7及其相关配置
    istringstream是支持>>一个bool的,但为什么不用?
    用vector实现一个变长数组
    C语言为什么被const声明的变量不是一个常量表达式
    不咬文嚼字的理由
    int变量赋值给char变量的本质
    #include <> 和 #include "" 的区别
    C++中匿名对象应当是一个左值
    js实战之-间断文字滑动
  • 原文地址:https://www.cnblogs.com/whx1003/p/14981104.html
Copyright © 2011-2022 走看看