「PKUWC2018」獵人殺

「PKUWC2018」獵人殺

解題思路c++

首先有一個很妙的結論是問題能夠轉化爲已經死掉的獵人繼續算在機率裏面,每一輪一直開槍直到射死一個以前沒死的獵人爲止。git

證實,設全部獵人的機率之和爲 \(W\) ,當前已經死掉了機率之和爲 \(T\) 的獵人,原問題下一個射死 \(i\) 的機率 \(P\)
\[ P =\dfrac{w_i}{W-T} \]
轉化事後的問題下一個射死 \(i\) 的機率爲
\[ P=\dfrac{T}{W}P+\dfrac{w_i}{W} \\ \dfrac{W-T}{W}P=\dfrac{w_i}{W} \\ P=\dfrac{w_i}{W-T} \]
兩個問題的機率是同樣的。函數

而後考慮經典容斥,欽定一個不包含 \(1\) 的獵人集合 \(S\)\(1\) 以後被射死,用 \(sum(S)\) 表示這個獵人集合的 \(w_i\) 之和,那麼答案就是:spa

\[ Ans =\sum_S (-1)^{|S|}\sum_{i=0}^{\infty} (1-\frac{sum(S)+w_1}{W})^i\frac{w_1}{W}\\ \]code

考慮到 \(\sum_{i=0}^{\infty}(1-\dfrac{sum(S)+w_1}{W})^i\) 是收斂的,因此
\[ Ans =\sum _{S}(-1)^{|S|}\frac{W}{sum(S)+w_1}\times \frac{w_1}W{}\\ Ans = w_1\sum _{S}\dfrac{(-1)^{|S|}}{sum(S)+w_1} \]
構造生成函數 \([x^n]F(x)\)\(sum(S)=n\) 的全部方案的 \((-1)^{|S|}\) 之和,那麼有
\[ F(x)=\prod_{i=2}^n(1-x^{w_i}) \\ Ans = w_1\sum_{i=0}^{W-w_1}\dfrac{[x^i]F(x)}{i+w_1} \]
由於 \(\sum w_i\) 不大,分治 NTT 求解便可,複雜度 \(\mathcal O(n \log^2 n)\),能夠對這個式子 \(\ln\) 一下再考慮泰勒展開形式把有用的項記下來 \(\exp\) 回去,估計跑不過兩個 \(\log\)get

code

/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int ch = 0, f = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
const int N = (1 << 21), P = 998244353, G = 3;
namespace poly{
    int rev[N], len, lg;
    inline int Pow(int a, int b){
        int ans = 1;
        for(; b; b >>= 1, a = 1ll * a * a % P)
            if(b & 1) ans = 1ll * ans * a % P;
        return ans;
    }
    inline void timesinit(int lenth){
        for(len = 1, lg = 0; len <= lenth; len <<= 1, lg++);
        for(int i = 0; i < len; i++)
            rev[i] = (rev[i>>1] >> 1) | ((i & 1) << (lg - 1));
    }
    inline void DFT(int *a, int sgn){
        for(int i = 0; i < len; i++) 
            if(i < rev[i]) swap(a[i], a[rev[i]]);
        for(int k = 2; k <= len; k <<= 1){
            int w = Pow(G, (P - 1) / k);
            if(sgn == -1) w = Pow(w, P - 2);
            for(int i = 0; i < len; i += k){
                int now = 1;
                for(int j = i; j < i + (k >> 1); j++){
                    int x = a[j], y = 1ll * now * a[j+(k>>1)] % P;
                    a[j] = x + y >= P ? x + y - P : x + y;
                    a[j+(k>>1)] = x - y < 0 ? x - y + P : x - y;
                    now = 1ll * now * w % P;
                }
            }
        }
        if(sgn == -1){
            int INV = Pow(len, P - 2);
            for(int i = 0; i < len; i++) a[i] = 1ll * a[i] * INV % P;
        }
    }
}
using poly::Pow;
using poly::DFT;
using poly::timesinit;
int a[N], b[N], w[N], n;
inline vector<int> solveNTT(int l, int r){
    if(l == r){
        vector<int> vec; 
        vec.resize(w[l] + 1), vec[0] = 1, vec[w[l]] = P - 1;
        return vec;
    }
    int mid = (l + r) >> 1;
    vector<int> A = solveNTT(l, mid);
    vector<int> B = solveNTT(mid + 1, r);
    int lenth = (int) A.size() + (int) B.size() - 1;
    for(int i = 0; i < (int) A.size(); i++) a[i] = A[i];
    for(int i = 0; i < (int) B.size(); i++) b[i] = B[i];
    timesinit(lenth);
    DFT(a, 1), DFT(b, 1);
    for(int i = 0; i < poly::len; i++) a[i] = 1ll * a[i] * b[i] % P;
    DFT(a, -1);
    vector<int> vec;
    for(int i = 0; i < lenth; i++) vec.push_back(a[i]);
    for(int i = 0; i < poly::len; i++) a[i] = b[i] = 0;
    return vec;
}
int main(){
    read(n);
    if(n == 1) return puts("1"), 0;
    for(int i = 1; i <= n; i++) read(w[i]);
    vector<int> ans = solveNTT(2, n);
    int Ans = 0;
    for(int i = 0; i < (int) ans.size(); i++)
        (Ans += 1ll * ans[i] * Pow(i + w[1], P - 2) % P) %= P;
    cout << 1ll * w[1] * Ans % P << endl;
    return 0;
}
相關文章
相關標籤/搜索