THUWC2018 猎人杀

https://loj.ac/problem/2541

看了题解

题目大意

题解

考虑一下:对于一个集合 \(S\),如何计算 \(S\) 中的人必须在 \(1\) 号之后被杀(不在集合内的也可能在 \(1\) 之后被杀)的概率 \(P(S)\)。

\[
A = \sum_{i} w_i \B = \sum_{i \in S} w_i
\]

那么

\[
P(S) = \sum_{i=0}^{\infty} {(\frac{A-B-w_1}{A})^i} \cdot \frac{w_1}{A} \= w_1 \cdot \frac{1}{B+w_1}
\]

容斥一下,要求没有人在 \(1\) 之后被杀的概率,答案为

\[
w_1 \cdot \sum_{S} (-1)^{|S|} \frac{1}{B + w_1}
\]

这个可以 DP 计算,令 \(dp[i][j]\) 为考虑了前 \(i\) 个人,集合的和为 \(j\) 的方案数,转移有两种:

\[
dp[i][j] \rightarrow dp[i+1][j] \-dp[i][j] \rightarrow dp[i+1][j+w[i]]
\]

这个可以看成一堆形如 \((1 - x^{w_i})\) 的多项式连乘,开个堆每次拿次数小的一个一个合并就好了,复杂度好像是 \(O(N \lg N)\)?

实现

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int, int> P;

const int MD = 998244353;
const int G = 3;

int pow_mod(int x, int n) {
    int r = 1;
    while (n) {
        if (n & 1) r = ll(r) * x % MD;
        x = ll(x) * x % MD;
        n >>= 1;
    }
    return r;
}

namespace ntt {
    const int S = 17;
    const int NN = 1 << S;
    static int rev[NN];
    void init() {
        for (int i = 1; i < NN; i++) {
            rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (S - 1));
        }
    }

    void fft(vector<int> &a) {
        int N = int(a.size());
        if (N == 1) return;
        int s = S - __builtin_ctz(N);

        for (int i = 0; i < N; i++) {
            int r = rev[i] >> s;
            if (i < r) swap(a[i], a[r]);
        }

        vector<int> root(N / 2);
        root[0] = 1;
        for (int k = 1; k < N; k *= 2) {
            root[1] = pow_mod(G, (MD - 1) / (k << 1));
            for (int i = 2; i < k; i++) {
                root[i] = ll(root[i - 1]) * root[1] % MD;
            }

            for (int i = 0; i < N; i += 2 * k) {
                for (int j = 0; j < k; j++) {
                    int d = ll(root[j]) * a[i + j + k] % MD;
                    a[i + j + k] = a[i + j] + MD - d;
                    if (a[i + j + k] >= MD) a[i + j + k] -= MD;
                    a[i + j] = a[i + j] + d - MD;
                    if (a[i + j] < 0) a[i + j] += MD;
                }
            }
        }
    }

    vector<int> pmul(const vector<int> &a, const vector<int> &b) {
        int A = int(a.size()), B = int(b.size());
        int N = 1;
        while (N < A + B - 1) N *= 2;
        vector<int> ac(N), bc(N);
        for (int i = 0; i < A; i++) ac[i] = a[i];
        for (int i = 0; i < B; i++) bc[i] = b[i];
        fft(ac);
        fft(bc);
        int ni = pow_mod(N, MD - 2);
        for (int i = 0; i < N; i++) {
            ac[i] = ll(ac[i]) * bc[i] % MD * ni % MD;
        }
        reverse(ac.begin() + 1, ac.end());
        fft(ac);
        ac.resize(A + B - 1);
        return ac;
    }
}
using ntt::pmul;

int n;
vector< vector<int> > p;
vector<int> w;

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    ntt::init();
    cin >> n;
    p = vector< vector<int> >(n);
    w = vector<int>(n);
    int sw = 0;
    for (int i = 0; i < n; i++) {
        cin >> w[i];
        sw += w[i];
    }

    priority_queue< P, vector<P>, greater<P> > que;
    for (int i = 1; i < n; i++) {
        p[i] = vector<int>(w[i] + 1);
        p[i][0] = 1;
        p[i][w[i]] = MD - 1;
        que.push(P(w[i] + 1, i));
    }
    while (int(que.size()) > 1) {
        int i = que.top().second; que.pop();
        int j = que.top().second; que.pop();
        p[i] = pmul(p[i], p[j]);
        que.push(P(int(p[i].size()), i));
        vector<int>().swap(p[j]);
    }

    vector<int> f = p[que.top().second];
    int ans = 0;
    for (int i = 0; i < int(f.size()); i++) {
        int inv = pow_mod(i + w[0], MD - 2);
        ans += ll(inv) * f[i] % MD;
        if (ans >= MD) ans -= MD;
    }
    cout << ll(ans) * w[0] % MD << ‘\n‘;
    return 0;
}

原文地址:https://www.cnblogs.com/hfccccccccccccc/p/10160918.html

时间: 2024-10-08 14:24:05

THUWC2018 猎人杀的相关文章

Loj #2541「PKUWC2018」猎人杀

Loj #2541. 「PKUWC2018」猎人杀 题目链接 好巧妙的题! 游戏过程中,概率的分母一直在变化,所以就非常的不可做. 所以我们将问题转化一下:我们可以重复选择相同的猎人,只不过在一个猎人被选择了过后我们就给他打上标记,再次选择他的时候就无效.这样与原问题是等价的. 证明: 设\(sum=\sum_iw_i,kill=\sum_{i被杀死了}w_i\). 攻击到未被杀死的猎人\(i\)的概率为\(P\). 则根据题意\(P=\frac{w_i}{sum-kill}\). 问题转化后:

「PKUWC2018」猎人杀(分治NTT+概率期望)

Description 猎人杀是一款风靡一时的游戏"狼人杀"的民间版本,他的规则是这样的: 一开始有 \(n\) 个猎人,第 \(i\) 个猎人有仇恨度 \(w_i\) ,每个猎人只有一个固定的技能:死亡后必须开一枪,且被射中的人也会死亡. 然而向谁开枪也是有讲究的,假设当前还活着的猎人有 \([i_1,i_2,...,i_m]\),那么有 \(\frac{w_{i_k}}{\sum_{j=1}^nw_{i_j}}\) 的概率是向猎人 \(k\) 开枪. 一开始第一枪由你打响,目标的选

【PKUWC2018】猎人杀

题目描述 题目分析 设\(W=\sum\limits_{i=1}^nw_i\),\(A=\sum\limits_{i=1}^nw_i[i\ is\ alive]\),\(P_i\)为下一个打中\(i\)的概率. 如果开枪打中了已经死亡的猎人,我们可以视作再开一枪,这样就不会产生影响,因此有 \[ \begin{split} P_i&=\frac{W-A}{W}P_i+\frac{w_i}W\移项得\ P_i&=\frac{w_i}{A} \end{split} \] 考虑容斥,枚举\(S\

loj2541 「PKUWC2018」猎人杀

https://loj.ac/problem/2541 自己是有多菜啊,10天前做的题,当时还是看了题解,还让NicoDafaGood同学给我讲了一下. 而我现在忘得一干二净,一点都想不起来了…… 主要是当时听懂了就打了,没有总结啊. 我们发现,我们设集合$A$的$w$之和是$S_A$ 那么一个集合$A$在1之后死的概率是$\frac{w_1}{S_A+w_1}$. 为什么呢. 虽然每次选下一个会死的人,是从没死的人中选,但是实际上,也可以是所有人中选,如果选到了死了的人就继续选. 记得很久以前

luoguP5644 [PKUWC2018]猎人杀 概率期望+生成函数+NTT

好神仙的概率题啊 我感觉第一步转化好难想,但是想出来第一步的转化方法的话后面用生成函数算背包就很简单了. code: #include <cmath> #include <cstring> #include <algorithm> #include <cstdio> #include <string> #define ll long long #define ull unsigned long long using namespace std;

「PKUWC2018」猎人杀(概率+容斥+分治NTT)

https://loj.ac/problem/2541 很有意思的一道题目. 直接去算这题话,因为分母会变,你会发现不管怎么样都要枚举顺序. 考虑把题目转换,变成分母不会变的,即对于一个已经删过的,我们不把它从分母中剔除,但是,每一次的选择需要一直选直到选了一个没有被删过的. 然后再考虑怎么计算,这时就可以容斥了: 1既然要最后删除,我们枚举一个集合S一定在它之后被删,其它的随意. 设\(sw\)为\(\sum_{i\in S}w[i]\),\(W=\sum_{i=1}^n w[i]\) 最后答

PKUWC2018划水记

半夜在宾馆里睡不着,被潜意识叫起来写作文. 至于为什么标题是划水呢?因为冰也是水的一种形态,而长沙正好又下雪,所以你懂的. Day -1(1.28) 考前疯狂复习模板,正好前一天晚上打了一发LCT维护最小生成树的裸题.MD这算法果然是小C的一生之敌,做了几次link和cut操作之后总有节点开始按捺不住欲望开始做一些有悖伦理的事情,在手画了N(N>10)棵平衡树之后,终于发现是寻找一棵splay中最左的节点的时候必须一路down下去.于是放弃了直接维护最左节点的想法,改了一发,果然A了. 看一眼时

PKUWC 2018 铁牌记

Day –INF: 联赛后根据分数一部分人继续停课.由于本蒟蒻撞上了狗屎运,联赛分数还行,可参加NOIWC和PKUWC,故继续停课训练.期间补全了一堆知识点,并成功翘掉期末考.(然而该还的还是要还的,春节要恶补文化课了) 我们学校今年参加thuwc的人数为7人,pkuwc的人数为3人,随行的还有10位家长和2位教练,人数史上最多. 作为一个从未在冬天去过“北方”的人,考虑到长沙的气温将远低过广州,特意去买了两件羽绒服.我妈甚至想给我买一双防水的运动鞋因为没有找到合适的故没买,个人觉得没必要. P

YCB 的暑期计划

前言 YCB现在很弱(TAT) 暑假有一个月,赶快狂补一下. 大概的计划如下: 首先前期会以数据结构为主,毕竟代码能力太弱,涉及内容:线段树分治.二进制分组.KD-Tree. 等数据结构做到没有智商的时候加入一波数论,内容为 杜教筛.min_25筛. 然后中途小清新一下,做一些 组合博弈与构造题. 接着继续练代码能力,顺便学一些神奇的暴力:启发式合并.dsu on tree . 然后图论也忘的差不多了,就回过头去学点新东西,大概会有spfa判负环.0/1分数规划.差分约束. 估计这个时候也没有什