Loj #2541「PKUWC2018」猎人杀

Loj #2541. 「PKUWC2018」猎人杀

题目链接

好巧妙的题!

游戏过程中,概率的分母一直在变化,所以就非常的不可做。

所以我们将问题转化一下:我们可以重复选择相同的猎人,只不过在一个猎人被选择了过后我们就给他打上标记,再次选择他的时候就无效。这样与原问题是等价的。

证明:

设\(sum=\sum_iw_i,kill=\sum_{i被杀死了}w_i\)。

攻击到未被杀死的猎人\(i\)的概率为\(P\)。

则根据题意\(P=\frac{w_i}{sum-kill}\)。

问题转化后:
\[
\\P=\frac{kill}{sum}P+\frac{w_i}{sum}\\Rightarrow P=\frac{w_i}{sum-kill}。
\]
然后我们考虑容斥:枚举集合\(T\)中的猎人一定在\(1\)之后被杀死,其他猎人随意。

我们设\(S=\sum_{i\in T}w_i\)

则:
\[
\displaystyle
\begin{align}
ans&=(-1)^{|T|}\sum_{i=0}^{\infty}(1-\frac{S+w_1}{sum})^i\frac{w_1}{sum}
\\&=(-1)^{|T|}\frac{1}{1-(1-\frac{S+w_1}{sum})}\frac{w_1}{sum}
\\&=(-1)^{|T|}\frac{w_1}{w_1+S}
\end{align}
\]
然后我们就可以用背包背出所有\(\sum w_i\)恰好为\(S\)的带上容斥系数的方案数。

但复杂度有点高,于是我们考虑用生成函数来优化。这道题的生成函数还是比较简单,就是\(\Pi (1-x^{w_i})\)。用分治\(NTT\)实现。

代码:

#include<bits/stdc++.h>
#define ll long long
#define mod 998244353
#define N 100005

using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

ll ksm(ll t,ll x)  {
    ll ans=1;
    for(;x;x>>=1,t=t*t%mod)
        if(x&1) ans=ans*t%mod;
    return ans;
}

int rev[N<<2];
void NTT(ll *a,int d,int flag) {
    static ll G=3;
    int n=1<<d;
    for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<d-1);
    for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
    for(int s=1;s<=d;s++) {
        int len=1<<s,mid=len>>1;
        ll w=flag==1?ksm(G,(mod-1)/len):ksm(G,mod-1-(mod-1)/len);
        for(int i=0;i<n;i+=len) {
            ll t=1;
            for(int j=0;j<mid;j++,t=t*w%mod) {
                ll u=a[i+j],v=a[i+j+mid]*t%mod;
                a[i+j]=(u+v)%mod;
                a[i+j+mid]=(u-v+mod)%mod;
            }
        }
    }
    if(flag==-1) {
        ll inv=ksm(n,mod-2);
        for(int i=0;i<n;i++) a[i]=a[i]*inv%mod;
    }
}

ll f[N];
int n,w[N];
int sum[N];
int binary(int lx,int rx) {
    int mid,l=lx,r=rx;
    while(l<r) {
        mid=l+r+1>>1;
        if(sum[mid]-sum[lx-1]<=sum[rx]-sum[mid]) l=mid;
        else r=mid-1;
    }
    return l;
}

void solve(int l,int r,ll *f) {
    if(l==r) {
        f[0]=1;
        f[w[l]]=mod-1;
        return ;
    }
    int mid=binary(l,r);
    const int d=ceil(log2(sum[r]-sum[l-1]))+1;
    ll *a=new ll[(1<<d)+5],*b=new ll[(1<<d)+5];
    for(int i=0;i<(1<<d);i++) a[i]=b[i]=0;
    solve(l,mid,a),solve(mid+1,r,b);
    NTT(a,d,1),NTT(b,d,1);
    for(int i=0;i<(1<<d);i++) a[i]=a[i]*b[i]%mod;
    NTT(a,d,-1);
    for(int i=0;i<(1<<d);i++) f[i]=a[i];
    delete a;
    delete b;
}

ll ans;
int main() {
    n=Get();
    for(int i=1;i<=n;i++) w[i]=Get();
    sort(w+2,w+1+n);
    for(int i=2;i<=n;i++) sum[i]=sum[i-1]+w[i];
    solve(2,n,f);
    int tot=sum[n]-sum[1];
    for(int i=0;i<=tot;i++) {
        (ans+=f[i]*w[1]%mod*ksm(w[1]+i,mod-2)%mod)%=mod;
    }
    cout<<ans;
    return 0;
}

原文地址:https://www.cnblogs.com/hchhch233/p/10159821.html

时间: 2024-08-30 09:13:19

Loj #2541「PKUWC2018」猎人杀的相关文章

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

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

loj2541 「PKUWC2018」猎人杀

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

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

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

Loj #2542. 「PKUWC2018」随机游走

Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次询问给定一个集合 \(S\),求如果从 \(x\) 出发一直随机游走,直到点集 \(S\) 中所有点都至少经过一次的话,期望游走几步. 特别地,点 \(x\)(即起点)视为一开始就被经过了一次. 答案对 $998244353 $ 取模. 输入格式 第一行三个正整数 \(n,Q,x\). 接下来 \(

loj#2537. 「PKUWC2018」Minimax

传送门 感觉我去pkuwc好像只有爆零的份-- 设\(f_{u,i}\)表示\(u\)取到\(i\)的概率,那么有如下转移 \[f_{u,i}=f_{ls,i}(p_u\sum_{j<i}f_{rs,j}+(1-p_u)\sum_{j>i}f_{rs,j})+\\f_{rs,i}(p_u\sum_{j<i}f_{ls,j}+(1-p_u)\sum_{j>i}f_{ls,j})\] 然后用线段树合并即可,最后在根节点的线段树上\(dfs\)统计答案 //minamoto #inclu

loj#2540. 「PKUWC2018」随机算法

传送门 完了pkuwc咋全是dp怕是要爆零了-- 设\(f(S)\)表示\(S\)的排列数,\(S\)为不能再选的点集(也就是选到独立集里的点和与他们相邻的点),\(mx(S)\)表示\(S\)状态下对应的独立集大小,枚举点\(i\),如果\(i\)不在\(S\)里,分情况考虑,设\(w[i]\)表示点\(i\)以及与之相邻的点,\(T=S|w[i]\),\(sz[S]\)表示二进制\(S\)有多少个\(1\),如果\(mx[T]=mx[S]+1\),那么\[f[T]+=f[S]\times A

loj#2552. 「CTSC2018」假面

题目链接 loj#2552. 「CTSC2018」假面 题解 本题严谨的证明了我菜的本质 对于砍人的操作好做找龙哥就好了,blood很少,每次暴力维护一下 对于操作1 设\(a_i\)为第i个人存活的概率,\(d_i\)为死掉的概率,\(g_{i,j}\)是除i以外活了j个人的概率 那个选中i人的答案就是 \[a_i\times\sum_{j = 0} ^{k - 1}\frac{g_{i,j}}{j + 1}\] 对于\(g_{i,j}\) ,设\(f_{i,j}\)表示前\(i\)个人有\(

loj#2076. 「JSOI2016」炸弹攻击 模拟退火

目录 题目链接 题解 代码 题目链接 loj#2076. 「JSOI2016」炸弹攻击 题解 模拟退火 退火时,由于答案比较小,但是温度比较高 所以在算exp时最好把相差的点数乘以一个常数让选取更差的的概率降低 代码 #include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #define gc getchar() #define

Loj #2192. 「SHOI2014」概率充电器

Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决定!SHOI 概率充电器,您生 活不可或缺的必需品!能充上电吗?现在就试试看吧!」 SHOI 概率充电器由 \(n-1\) 条导线连通了 \(n\) 个充电元件.进行充电时,每条导线是否可以导电以 概率决定,每一个充电元件自身是否直接进行充电也由概率决定.随后电能可以从直接充电的元件经