题面传送门

很久之前(2020 年)就听说过这题了,这么经典的题怎么能只听说而亲自做一遍呢

首先注意到每次开枪打死一个猎人之后,打死其他猎人概率的分母就会发生变化,这将使我们维护起来非常棘手,因此我们考虑做一个转化:每次随便从全集中选出一个猎人(不管死的活的),如果它是活的就将它射死。假设现在死了的猎人的 \(w_i\) 值之和为 \(T\),所有猎人的 \(w_i\) 值之和为 \(U\),那么精通无穷级数的同学应该不难推出,对于某个还活着的猎人 \(j\),射到的第一个活着的猎人是 \(j\) 的概率就是 \(\sum\limits_{i=0}^{\infty}(\dfrac{T}{U})^i·\dfrac{w_j}{U}=\dfrac{U}{U-T}·\dfrac{w_j}{U}=\dfrac{w_j}{U-T}\),刚好就是题目中的式子。

这样一来我们就大可不必考虑“每一枪射到的猎人必须是活的”这个限制了,接下来考虑原问题。考虑容斥(没想到*1),我们考虑钦定一个集合 \(S(1\notin S)\) 并令 \(S\) 中的猎人必须在 \(1\) 之后死,我们记这样的概率为 \(p(S)\),那么答案显然就是 \(\sum\limits_{1\notin S}p(S)(-1)^{|S|}\)。考虑这个 \(p(S)\) 是个什么东西,按照上面的转化,\(S\) 中的猎人在 \(1\) 之后死即意味着在打死 \(1\) 之前选择的猎人都不在 \(S\) 中,那么我们可以枚举打死 \(1\) 之前开了多少枪,设这个数是 \(c\),方便起见我们假设 \(X=\sum\limits_{x\in S}w_x\),那么可列出方程 \(p(S)=\sum\limits_{c=0}^{\infty}(\dfrac{U-X-w_1}{U})^c·\dfrac{w_1}{U}=\dfrac{U}{X+w_1}·\dfrac{w_1}{U}=\dfrac{w_1}{X+w_1}\)。

琢磨清楚 \(p(S)\) 是个什么东西之后,最后一步就是计算上面那个式子了。暴力枚举 \(S\) 显然 T 飞,想也别想了。不过一个 observation 是 \(p(S)\) 的表达式只与 \(S\) 中所有元素的 \(w\) 值之和 \(X\) 有关,因此我们考虑枚举 \(X\),即 \(ans=\sum\limits_{X}\dfrac{w_1}{X+w_1}\sum\limits_{S}(-1)^{|S|}[\sum\limits_{x\in S}w_x=X]\),也就是说如果我们能求出所有满足 \(\sum\limits_{x\in S}w_x=X\) 的 \((-1)^{|S|}\) 之和那这题就搞定了。这东西怎么求呢?这东西看起来好像有点眼熟,\(w_x\) 之和等于 \(X\) 可以看作……系数之和等于 \(X\),对!生成函数(想不到 *2,u1s1 中考结束后 wtm 简直像个 sb)。我们令 \(F(x)=\prod\limits_{i=2}^n(1-x^{w_i})\),那么这东西就是 \([x^{X}]F(x)\),由于 \(\sum\limits_{i=1}^nw_i\le 10^5\),因此可以分治+NTT(为什么是“分治+NTT”而不是“分治 NTT”呢?因为这里的分治不是 cdq 分治)求出 \(F(x)\),时间复杂度 \(n\log^2n\)

const int MAXN=1e5;
const int MAXP=1<<18;
const int pr=3;
const int MOD=998244353;
const int ipr=(MOD+1)/3;
int n,a[MAXN+5];
int qpow(int x,int e){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int rev[MAXP+5];
void NTT(vector<int> &a,int len,int type){
int lg=31-__builtin_clz(len);
for(int i=0;i<len;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<lg-1);
for(int i=0;i<len;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int i=2;i<=len;i<<=1){
int W=qpow((type<0)?ipr:pr,(MOD-1)/i);
for(int j=0;j<len;j+=i){
for(int k=0,w=1;k<(i>>1);k++,w=1ll*w*W%MOD){
int X=a[j+k],Y=1ll*a[(i>>1)+j+k]*w%MOD;
a[j+k]=(X+Y)%MOD;a[(i>>1)+j+k]=(X-Y+MOD)%MOD;
}
}
}
if(!~type){
int ivn=qpow(len,MOD-2);
for(int i=0;i<len;i++) a[i]=1ll*a[i]*ivn%MOD;
}
}
vector<int> conv(vector<int> a,vector<int> b,int len){
int LEN=1;while(LEN<a.size()+b.size()) LEN<<=1;
a.resize(LEN,0);b.resize(LEN,0);NTT(a,LEN,1);NTT(b,LEN,1);
for(int i=0;i<LEN;i++) a[i]=1ll*a[i]*b[i]%MOD;
NTT(a,LEN,-1);while(a.size()>len) a.pop_back();return a;
}
vector<int> solve(int l,int r){
if(l==r){
vector<int> res(a[l]+1,0);
res[a[l]]=MOD-(res[0]=1);
return res;
} int mid=l+r>>1;
vector<int> L=solve(l,mid);
vector<int> R=solve(mid+1,r);
return conv(L,R,L.size()+R.size()-1);
}
int main(){
scanf("%d",&n);if(n==1) return puts("1")&0;int sum=0;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=!!(i^1)*a[i];
vector<int> res=solve(2,n);int ans=0;
for(int i=0;i<=sum;i++) ans=(ans+1ll*a[1]*qpow(a[1]+i,MOD-2)%MOD*res[i])%MOD;
printf("%d\n",ans);
return 0;
}

洛谷 P5644 - [PKUWC2018]猎人杀(分治+NTT)的更多相关文章

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

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

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

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

  3. P5644-[PKUWC2018]猎人杀【NTT,分治】

    正题 题目链接:https://www.luogu.com.cn/problem/P5644 题目大意 \(n\)个人,每个人被选中的权重是\(a_i\).每次按照权重选择一个没有死掉的人杀死,求第\ ...

  4. 洛谷 4721 【模板】分治 FFT——分治FFT / 多项式求逆

    题目:https://www.luogu.org/problemnew/show/P4721 分治FFT:https://www.cnblogs.com/bztMinamoto/p/9749557.h ...

  5. 洛谷SP22343 NORMA2 - Norma(分治,前缀和)

    洛谷题目传送门 这题推式子恶心..... 考虑分治,每次统计跨过\(mid\)的所有区间的答案和.\(i\)从\(mid-1\)到\(l\)枚举,统计以\(i\)为左端点的所有区间. 我们先维护好\( ...

  6. Poj1741/洛谷P4718 Tree(点分治)

    题面 有多组数据:Poj 无多组数据:洛谷 题解 点分治板子题,\(calc\)的时候搞一个\(two\ pointers\)扫一下统计答案就行了. #include <cmath> #i ...

  7. 洛谷P3810 陌上花开(CDQ分治)

    洛谷P3810 陌上花开 传送门 题解: CDQ分治模板题. 一维排序,二维归并,三维树状数组. 核心思想是分治,即计算左边区间对右边区间的影响. 代码如下: #include <bits/st ...

  8. 洛谷P4705 玩游戏 [生成函数,NTT]

    传送门 这是两个月之前写的题,但没写博客.现在回过头来看一下发现又不会了-- 还是要写博客加深记忆. 思路 显然期望可以算出总数再乘上\((nm)^{-1}\). 那么有 \[ \begin{alig ...

  9. 题解-PKUWC2018 猎人杀

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

随机推荐

  1. javascriptRemke之类的继承

    前言:es6之前在js中要实现继承,就必须要我们程序员在原型链上手动继承多对象的操作,但是结果往往存在漏洞,为解决这些问题,社区中出现了盗用构造函数.组合继承.原型式继承.寄生式继承等一系列继承方式, ...

  2. NB-IoT的DRX、eDRX、PSM三个模式怎么用?通俗解释,看完就懂!

    面我们讲了不少NB-IOT的应用.软件和硬件设计的变动. (链接在文章末尾). 今天讲讲NB-IoT的三大模式,在各种物联网和智能硬件场景中的使用方法 DRX.eDRx.PSM是什么? DRX虽然叫做 ...

  3. 前端大牛带你了解JavaScript 函数式编程

    前言 函数式编程在前端已经成为了一个非常热门的话题.在最近几年里,我们看到非常多的应用程序代码库里大量使用着函数式编程思想. 本文将略去那些晦涩难懂的概念介绍,重点展示在 JavaScript 中到底 ...

  4. Spring Security:Authentication 认证(一)

    1. Spring Security 简介 在 Spring 生态系统中,为他的项目增加安全性,你可以借助 Spring Security 库来做到这一点. 那什么是 Spring Security? ...

  5. linux tr

    转载:tr命令_Linux tr 命令用法详解:将字符进行替换压缩和删除 (linuxde.net) tr命令 文件过滤分割与合并 tr命令可以对来自标准输入的字符进行替换.压缩和删除.它可以将一组字 ...

  6. 数组中重复的数字 牛客网 剑指Offer

    数组中重复的数字 牛客网 剑指Offer 题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中 ...

  7. 空格替换 牛客网 程序员面试金典 C++ Python

    空格替换 牛客网 程序员面试金典 C++ Python 题目描述 请编写一个方法,将字符串中的空格全部替换为"%20".假定该字符串有足够的空间存放新增的字符,并且知道字符串的真实 ...

  8. 测试平台系列(72) 了解ApScheduler基本用法

    大家好~我是米洛! 我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的完整教程,希望大家多多支持. 欢迎关注我的公众号测试开发坑货,获取最新文章教程! 回顾 上一节我们调研了一下市面上 ...

  9. Java测试开发--Comparable和Comparator接口(五)

    Comparable 简介Comparable 是排序接口.若一个类实现了Comparable接口,就意味着"该类支持排序".此外,"实现Comparable接口的类的对 ...

  10. TLFS 内存分配算法详解

    文章目录 1. DSA 背景介绍 1.1 mmheap 1.2 mmblk 2. TLFS 原理 2.1 存储结构 2.2 内存池初始化 2.3 free 2.4 malloc 参考资料 1. DSA ...