[PKUWC2018]猎人杀
题解
感觉是一道神题,想不出来
问最后\(1\)号猎人存活的概率
发现根本没法记录状态
每次转移的分母也都不一样
可以考虑这样一件事情:
如果一个人被打中了
那么不急于从所有人中将ta删除,而是给ta打上一个标记,然后继续保留
下一回合如果打中的是一个已经死掉的就继续打
直到打到一个活的为止
可以发现这玩意儿可以是一个无限的东西
那么什么东西是收敛的可以求无线项的值?
等比数列!
那么我们就可以将分母确定下来了
考虑一个容斥:
枚举一个集合\(S\)表示的是至少有这\(i\)个人在1号猎人被打死之后才被打死
用\(W\)表示选定的这个集合的权值和,\(w_1\)表示1号猎人的权值,\(Sum\)表示总权值和
那么这个东西对答案的贡献就是
\((-1)^{|S|}\sum_{i=0}^{inf}{(1-\frac{W+w_1}{Sum})^i\frac{w_1}{W+w_1}}\)
也就是前i枪去打那些没有被钦定的猎人,打完\(i\)枪之后一枪打死\(1\)号猎人的概率
这玩意儿化简一下,等比数列的和\(=\frac{首项}{1-公比}\)
化出来的就是这个东西\((-1)^{|S|}\sum_{i=0}^{inf}{\frac{w_1}{W+w_1}}\)
那么问题就是怎么计算这个集合的大小以及权值和
我们可以考虑背包
直接求出这种权值和的方案的系数
\(f[i][j]\)表示从前\(i\)个猎人中选择了权值和为\(j\)的系数
因为每次选择一个猎人都会使得符号发生改变
所以\(dp\)式子也就是\(f[i][j]=f[i-1][j]-f[i-1][j-w_i]\)
那么这样就可以得到一个\(O(n^2)\)的dp
考虑生成函数
通过上面的dp可以发现对于每一个点权\(w_i\)
,ta的生成函数就是\(1-x^{w_i}\)
那么答案就是\(\prod(1-x^{w_i})\)
分治一下写个\(NTT\)就过了
代码
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define LL long long
# define ls (now << 1)
# define rs (now << 1 | 1)
const int M = 400005 ;
const int mod = 998244353 ;
const int G = 3 ;
const int Gi = mod / G + 1 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x=x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
int n , m , ans ;
int len , lim = 1 , rev[M] , val[M] ;
LL inv[M][2] ;
vector < LL > vec[M] ;
inline LL Fpw(LL Base , LL k) {
int temp = 1 ;
while(k) {
if(k & 1) temp = temp * Base % mod ;
Base = Base * Base % mod ; k >>= 1 ;
}
return temp ;
}
inline void NTT(vector < LL > &A , int unit) {
for(int i = 0 ; i < lim ; i ++) if(rev[i] > i) swap(A[i] , A[rev[i]]) ;
for(int mid = 1 ; mid < lim ; (mid <<= 1)) {
int R = (mid << 1) ; LL W = inv[R][unit] ;
for(int j = 0 ; j < lim ; j += R) {
LL w = 1 ;
for(int k = 0 ; k < mid ; k ++ , w = (w * W) % mod) {
LL x = A[j + k] , y = w * A[j + k + mid] % mod ;
A[j + k] = (x + y) % mod ; A[j + k + mid] = (x - y) % mod ;
}
}
}
}
inline void pushup(int now) {
if(vec[ls].empty()) vec[now] = vec[rs] ;
else if(vec[rs].empty()) vec[now] = vec[ls] ;
else {
int sz = vec[ls].size() + vec[rs].size() ;
lim = 1 ; len = 0 ;
while(lim <= sz) lim <<= 1 , ++ len ;
for(int i = 0 ; i <= lim ; i ++) rev[i] = ((rev[i >> 1] >> 1) | ((i & 1) << (len - 1))) ;
vec[ls].resize(lim + 1) ; vec[rs].resize(lim + 1) ; vec[now].resize(lim + 1) ;
NTT(vec[ls] , 1) ; NTT(vec[rs] , 1) ;
for(int i = 0 ; i <= lim ; i ++)
vec[now][i] = (vec[ls][i] * vec[rs][i]) % mod ;
NTT(vec[now] , 0) ; LL tinv = Fpw(lim , mod - 2) ;
for(int i = 0 ; i <= sz ; i ++)
vec[now][i] = (vec[now][i] * tinv % mod + mod) % mod ;
vec[now].resize(sz) ;
}
vec[ls].clear() ; vec[rs].clear() ;
}
void Solve(int l , int r , int now) {
if(l == r) {
vec[now].resize(val[l] + 1) ;
vec[now][0] = 1 ; vec[now][val[l]] = -1 ;
return ;
}
int mid = (l + r) >> 1 ;
Solve(l , mid , ls) ;
Solve(mid + 1 , r , rs) ;
pushup(now) ;
}
int main() {
n = read() ;
for(int i = 1 ; i <= n ; i ++) {
val[i] = read() ;
if(i > 1) m += val[i] ;
}
for(int i = 1 ; i <= 400000 ; (i <<= 1)) {
inv[i][1] = Fpw(G , (mod - 1) / i) ;
inv[i][0] = Fpw(Gi , (mod - 1) / i) ;
}
Solve(2 , n , 1) ;
for(int i = 0 ; i <= m ; i ++) {
if(!vec[1][i]) continue ;
ans = ((ans + vec[1][i] * val[1] % mod * Fpw(i + val[1] , mod - 2) % mod) % mod + mod) % mod ;
}
printf("%d\n",ans) ;
return 0 ;
}
[PKUWC2018]猎人杀的更多相关文章
- LOJ2541 PKUWC2018 猎人杀 期望、容斥、生成函数、分治
传送门 首先,每一次有一个猎人死亡之后\(\sum w\)会变化,计算起来很麻烦,所以考虑在某一个猎人死亡之后给其打上标记,仍然计算他的\(w\),只是如果打中了一个打上了标记的人就重新选择.这样对应 ...
- LOJ2541 PKUWC2018猎人杀(概率期望+容斥原理+生成函数+分治NTT)
考虑容斥,枚举一个子集S在1号猎人之后死.显然这个概率是w1/(Σwi+w1) (i∈S).于是我们统计出各种子集和的系数即可,造出一堆形如(-xwi+1)的生成函数,分治NTT卷起来就可以了. #i ...
- 洛谷 P5644 - [PKUWC2018]猎人杀(分治+NTT)
题面传送门 很久之前(2020 年)就听说过这题了,这么经典的题怎么能只听说而亲自做一遍呢 首先注意到每次开枪打死一个猎人之后,打死其他猎人概率的分母就会发生变化,这将使我们维护起来非常棘手,因此我们 ...
- [LOJ2541][PKUWC2018]猎人杀(容斥+分治+FFT)
https://blog.csdn.net/Maxwei_wzj/article/details/80714129 n个二项式相乘可以用分治+FFT的方法,使用空间回收可以只开log个数组. #inc ...
- 【洛谷5644】[PKUWC2018] 猎人杀(容斥+生成函数+分治NTT)
点此看题面 大致题意: 有\(n\)个人相互开枪,每个人有一个仇恨度\(a_i\),每个人死后会开枪再打死另一个还活着的人,且第一枪由你打响.设当前剩余人仇恨度总和为\(k\),则每个人被打中的概率为 ...
- [LOJ2541] [PKUWC2018] 猎人杀
题目链接 LOJ:https://loj.ac/problem/2541 Solution 很巧妙的思路. 注意到运行的过程中概率的分母在不停的变化,这样会让我们很不好算,我们考虑这样转化:假设所有人 ...
- 题解-PKUWC2018 猎人杀
Problem loj2541 题意概要:给定 \(n\) 个人的倒霉度 \(\{w_i\}\),每回合会有一个人死亡,每个人这回合死亡的概率为 自己的倒霉度/目前所有存活玩家的倒霉度之和,求第 \( ...
- 「PKUWC2018」猎人杀
「PKUWC2018」猎人杀 解题思路 首先有一个很妙的结论是问题可以转化为已经死掉的猎人继续算在概率里面,每一轮一直开枪直到射死一个之前没死的猎人为止. 证明,设所有猎人的概率之和为 \(W\) , ...
- 【LOJ2541】【PKUWC2018】猎人杀(容斥,FFT)
[LOJ2541][PKUWC2018]猎人杀(容斥,FFT) 题面 LOJ 题解 这题好神仙啊. 直接考虑概率很麻烦,因为分母总是在变化. 但是,如果一个人死亡之后,我们不让他离场,假装给他打一个标 ...
随机推荐
- antd 离线 icon
讲你下载下来的官方提供的字体库解压后所有文件复制到node-modules/antd/dist目录下 创建新的文件夹iconfont 在你项目生成的css入口文件对应的源码less文件开始添加如下两句 ...
- TF-IDF与余弦类似性的应用(一):自己主动提取关键词
作者: 阮一峰 日期: 2013年3月15日 原文链接:http://www.ruanyifeng.com/blog/2013/03/tf-idf.html 这个标题看上去好像非常复杂,事实上我要谈的 ...
- ClassLoader如何加载class
jvm里有多个类加载,每个类加载可以负责加载特定位置的类, 例如: bootstrap类加载负责加载jre/lib/rt.jar中的类, 我们平时用的jdk中的类都位于rt.jar中. extclas ...
- Speech Recognition Grammar Specification Version 1.0 JavaScript TTS 文本发音
Speech Recognition Grammar Specification Version 1.0 https://www.w3.org/TR/speech-grammar/ W3C Recom ...
- python 线程 进程 标识
s = '%s%s%s%s%s%s%s%s' % ( time.strftime('%Y%m%d %H:%M:%S', time.localtime(time.time())), ' os.getpp ...
- YTU 2800: 逗逗泡泡的保密电文
2800: 逗逗泡泡的保密电文 时间限制: 1 Sec 内存限制: 128 MB 提交: 229 解决: 114 题目描述 某电报局的电文保密的规律是将每个英文字母变成其后的第4个字母(很原始的那 ...
- 《Visual C++ 2010入门教程》系列二:安装、配置和首次使用VS2010
作者:董波 日期:2010.6.15 写在前面 在我还在上学的时候,我选择了C++,最初我用VC6作为我的IDE,我看过很多本C++的教材,有的适合我,有的不适合我,其中有一本叫<Visual ...
- lucene DocValues——没有看懂
前言: 在Lucene4.x之后,出现一个重大的特性,就是索引支持DocValues,这对于广大的solr和elasticsearch用户,无疑来说是一个福音,这玩意的出现通过牺牲一定的磁盘空间带来的 ...
- 【HDU 1561】 The More,The better
[题目链接] 点击打开链接 [算法] 树形背包 注意是一棵森林 [代码] #include<bits/stdc++.h> using namespace std; #define MAXN ...
- Django 缓存 使用 Redis Memcached 为网站提速
RedisRedis是一种键值对类型的内存数据库,读写内存比读写硬盘快,我们在Django里面使用Redis非常方便,下面给出详细步骤 基于Ubuntu 1. 安装Redis和django-redis ...