传送门


首先,每一次有一个猎人死亡之后\(\sum w\)会变化,计算起来很麻烦,所以考虑在某一个猎人死亡之后给其打上标记,仍然计算他的\(w\),只是如果打中了一个打上了标记的人就重新选择。这样对应于每一个人的概率仍然是一样的,而\(\sum w\)在计算的过程中不会变。

因为要求最后死的概率,似乎不是很好求,考虑容斥。枚举一个集合\(S\),我们强制集合\(S\)中的猎人在\(1\)号猎人死亡之后死亡。设集合\(S\)中所有猎人的\(w\)之和为\(A\),所有猎人的\(w\)之和为\(sum\),那么集合\(S\)能够产生的贡献为\((-1) ^ {|S|} \times \frac{w_1}{sum} \times \sum\limits_{i=0} ^ {\infty} (1 - \frac{A + w_1}{sum})^i\)。

注意到后面是一个无穷递减等比数列,那么\(\sum\limits_{i=0} ^ {\infty} (1 - \frac{A + w_1}{sum})^i = \frac{1}{1 - (1 - \frac{A + w_1}{sum})} = \frac{sum}{A + w_1}\),那么原式等于\((-1)^{|S|} \times \frac{w_1}{A + w_1}\)。

那么我们只需要计算每一个集合的\(A\)就可以了。

注意到对于\(A\)的计算,实质是一个\(01\)背包。但是直接\(DP\)肯定复杂度爆炸,考虑生成函数求解

第\(i\)个猎人的生成函数为\(-x^{w_i} + 1\),\(-x^{w_i}\)表示选择第\(i\)个猎人,但是集合的贡献乘上\(-1\),\(+1\)表示不选择第\(i\)个猎人。然后分治\(FFT\)求解,我们就可以得到对于所有的\(A\)的\(\frac{w_1}{A + w_1}\)前面的系数了。

总的复杂度为\(O(n\ log^2n)\)

#include<bits/stdc++.h>
#define ll long long
#define mid ((l + r) >> 1)
//This code is written by Itst
using namespace std; inline int read(){
int a = 0;
char c = getchar();
bool f = 0;
while(!isdigit(c)){
if(c == '-')
f = 1;
c = getchar();
}
while(isdigit(c)){
a = (a << 3) + (a << 1) + (c ^ '0');
c = getchar();
}
return f ? -a : a;
} const int MOD = 998244353 , G = 3 , INV = 332748118 , MAXN = 2e5 + 10;
int val[MAXN] , dir[MAXN] , N , need , inv_need;
vector < int > v[MAXN]; inline int poww(ll a , int b){
int times = 1;
while(b){
if(b & 1)
times = times * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return times;
} inline void NTT(vector < int > &arr , int type){
while(arr.size() < need)
arr.push_back(0);
for(int i = 1 ; i < need ; ++i)
if(i < dir[i])
swap(arr[i] , arr[dir[i]]);
for(int i = 1 ; i < need ; i <<= 1){
int wn = poww(type == 1 ? G : INV , (MOD - 1) / (i << 1));
for(int j = 0 ; j < need ; j += i << 1){
ll w = 1;
for(int k = 0 ; k < i ; ++k , w = w * wn % MOD){
int x = arr[j + k] , y = arr[i + j + k] * w % MOD;
arr[j + k] = x + y >= MOD ? x + y - MOD : x + y;
arr[i + j + k] = x - y < 0 ? x - y + MOD : x - y;
}
}
}
} inline void solve(int l , int r){
need = 1;
while(need <= v[l].size() + v[r].size())
need <<= 1;
inv_need = poww(need , MOD - 2);
for(int i = 1 ; i < need ; ++i)
dir[i] = (dir[i >> 1] >> 1) | (i & 1 ? need >> 1 : 0);
NTT(v[l] , 1);
NTT(v[r] , 1);
for(int i = 0 ; i < need ; ++i)
v[l][i] = 1ll * v[l][i] * v[r][i] % MOD;
NTT(v[l] , -1);
for(int i = 0 ; i < need ; ++i)
v[l][i] = 1ll * v[l][i] * inv_need % MOD;
while(v[l][v[l].size() - 1] == 0)
v[l].erase(--v[l].end());
} int main(){
#ifndef ONLINE_JUDGE
freopen("in" , "r" , stdin);
//freopen("out" , "w" , stdout);
#endif
N = read();
if(N == 1){
puts("1");
return 0;
}
for(int i = 1 ; i <= N ; ++i){
val[i] = read();
if(i != 1){
v[i].push_back(1);
while(v[i].size() < val[i])
v[i].push_back(0);
v[i].push_back(MOD - 1);
}
}
int ans = 0;
for(int i = 1 ; i < N ; i <<= 1)
for(int j = 2 ; j + i <= N ; j += i << 1){
solve(j , j + i);
vector < int >().swap(v[j + i]);
}
for(int i = 0 ; i < v[2].size() ; ++i)
ans = (ans + 1ll * poww(i + val[1] , MOD - 2) * v[2][i]) % MOD;
cout << 1ll * ans * val[1] % MOD;
return 0;
}

LOJ2541 PKUWC2018 猎人杀 期望、容斥、生成函数、分治的更多相关文章

  1. 【洛谷5644】[PKUWC2018] 猎人杀(容斥+生成函数+分治NTT)

    点此看题面 大致题意: 有\(n\)个人相互开枪,每个人有一个仇恨度\(a_i\),每个人死后会开枪再打死另一个还活着的人,且第一枪由你打响.设当前剩余人仇恨度总和为\(k\),则每个人被打中的概率为 ...

  2. 【LOJ2541】【PKUWC2018】猎人杀(容斥,FFT)

    [LOJ2541][PKUWC2018]猎人杀(容斥,FFT) 题面 LOJ 题解 这题好神仙啊. 直接考虑概率很麻烦,因为分母总是在变化. 但是,如果一个人死亡之后,我们不让他离场,假装给他打一个标 ...

  3. LOJ2541 PKUWC2018猎人杀(概率期望+容斥原理+生成函数+分治NTT)

    考虑容斥,枚举一个子集S在1号猎人之后死.显然这个概率是w1/(Σwi+w1) (i∈S).于是我们统计出各种子集和的系数即可,造出一堆形如(-xwi+1)的生成函数,分治NTT卷起来就可以了. #i ...

  4. loj2541 「PKUWC2018」猎人杀 【容斥 + 分治NTT】

    题目链接 loj2541 题解 思路很妙啊, 人傻想不到啊 觉得十分难求,考虑容斥 由于\(1\)号可能不是最后一个被杀的,我们容斥一下\(1\)号之后至少有几个没被杀 我们令\(A = \sum\l ...

  5. [LOJ2541] [PKUWC2018] 猎人杀

    题目链接 LOJ:https://loj.ac/problem/2541 Solution 很巧妙的思路. 注意到运行的过程中概率的分母在不停的变化,这样会让我们很不好算,我们考虑这样转化:假设所有人 ...

  6. [LOJ2541][PKUWC2018]猎人杀(容斥+分治+FFT)

    https://blog.csdn.net/Maxwei_wzj/article/details/80714129 n个二项式相乘可以用分治+FFT的方法,使用空间回收可以只开log个数组. #inc ...

  7. [PKUWC2018]猎人杀

    题解 感觉是一道神题,想不出来 问最后\(1\)号猎人存活的概率 发现根本没法记录状态 每次转移的分母也都不一样 可以考虑这样一件事情: 如果一个人被打中了 那么不急于从所有人中将ta删除,而是给ta ...

  8. 题解-PKUWC2018 猎人杀

    Problem loj2541 题意概要:给定 \(n\) 个人的倒霉度 \(\{w_i\}\),每回合会有一个人死亡,每个人这回合死亡的概率为 自己的倒霉度/目前所有存活玩家的倒霉度之和,求第 \( ...

  9. 洛谷 P5644 - [PKUWC2018]猎人杀(分治+NTT)

    题面传送门 很久之前(2020 年)就听说过这题了,这么经典的题怎么能只听说而亲自做一遍呢 首先注意到每次开枪打死一个猎人之后,打死其他猎人概率的分母就会发生变化,这将使我们维护起来非常棘手,因此我们 ...

随机推荐

  1. 《Inside C#》笔记(二) 初识C#

    一 程序的编译.构成 a) 编写C#代码一般用VS,但作者在这儿介绍了使用记事本编写C#代码并编译运行的过程,以便对VS有更深入的认识. 用记事本编写C#代码后,修改文本文件的后缀为.cs,然后用cs ...

  2. Kotlin入门(4)声明与操作数组

    上一篇文章介绍了基本变量类型在Kotlin中的用法,不过这只针对单个变量,如果要求把一组相同类型的变量排列起来,形成一个变量数组,那又该如何声明和操作呢? 在Java中声明数组,跟在C语言中声明是一样 ...

  3. XSS(跨站脚本攻击)漏洞解决方案

    首先,简单介绍一下XSS定义: 一 . XSS介绍 XSS是跨站脚本攻击(Cross Site Scripting)的缩写.为了和层叠样式表CSS(Cascading Style Sheets)加以区 ...

  4. (后台)El表达式格式化两位小数

    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>引入标签库. < ...

  5. 11.1、socket连接中的粘包、精确传输问题

    粘包: 发生原因: 当调用send的时候,数据并不是即时发给客户端的.而是放到了系统的socket发送缓冲区里,等缓冲区满了.或者数据等待超时了,数据才会发送,所以有时候发送太快的话,前一份数据还没有 ...

  6. Python零基础学习系列之三--Python编辑器选择

    上一篇文章记录了怎么安装Python环境,同时也成功的在电脑上安装好了Python环境,可以正式开始自己的编程之旅了.但是现在又有头疼的事情,该用什么来写Python程序呢,该用什么来执行Python ...

  7. 安全之路 —— 借助DLL进行远程线程注入实现穿墙与隐藏进程

    简介        大多数后门或病毒要想初步实现隐藏进程,即不被像任务管理器这样典型的RING3级进程管理器找到过于明显的不明进程,其中比较著名的方法就是通过远程线程注入的方法注入将恶意进程的DLL文 ...

  8. pycharm的安装和使用小技巧

    一,pycharm 1,在官网下载pycharm,版本选5.0,一定要下载专业版. 2,注册方法:注册时选择 license server,填入:http://idea.qinxi1992.cn,然后 ...

  9. 团队Alpha博客链接目录

    Dipper团队Alpha博客链接目录 团队Alpha冲刺博客 第一次冲刺 第二次冲刺 第三次冲刺 第四次冲刺 第五次冲刺 第六次冲刺 第七次冲刺 第八次冲刺 第九次冲刺 第十次冲刺 第十一次冲刺 第 ...

  10. vue打包速度优化

    这是一个很头疼的问题,webpack极大的简化了前端自动化配置,但是打包速度实在是不如人意.在此之前,本人也尝试过网友的一些方法,但是,很多坑,跳进去就出不来,经过多个项目实践,现总结一下我用到的优化 ...