概述
猎人杀
题目描述
猎人杀是一款风靡一时的游戏“狼人杀”的民间版本,他的规则是这样的:
一开始有
n
n
个猎人,第 个猎人有仇恨度
wi
w
i
,每个猎人只有一个固定的技能:死亡后必须开一枪,且被射中的人也会死亡。
然而向谁开枪也是有讲究的,假设当前还活着的猎人有 [i1…im] [ i 1 … i m ] ,那么有 wik∑j=1mwij w i k ∑ j = 1 m w i j 的概率是向猎人 ik i k 开枪。
一开始第一枪由你打响,目标的选择方法和猎人一样(即有 wi∑j=1nwj w i ∑ j = 1 n w j 的概率射中第 i i 个猎人)。由于开枪导致的连锁反应,所有猎人最终都会死亡,现在 号猎人想知道它是最后一个死的的概率。
答案对 998244353 998244353 取模。
输入格式
第一行一个正整数 n n
第二行个正整数,第 i i 个正整数表示。
输出格式
输出答案。
样例
样例输入
3
1 1 2
样例输出
915057324
样例解释
答案是
24×12+14×23=1024
2
4
×
1
2
+
1
4
×
2
3
=
10
24
数据范围与提示
对于
10%
10
%
的数据,有
1≤n≤10
1
≤
n
≤
10
对于 30% 30 % 的数据,有 1≤n≤20 1 ≤ n ≤ 20
对于 50% 50 % 的数据,有 1≤∑i=1nwi≤5000 1 ≤ ∑ i = 1 n w i ≤ 5000
另有 10% 10 % 的数据,满足 1≤wi≤2 1 ≤ w i ≤ 2 ,且 w1=1 w 1 = 1
另有 10% 10 % 的数据,满足 1≤wi≤2 1 ≤ w i ≤ 2 ,且 w1=2 w 1 = 2
对于 100% 100 % 的数据,有 wi>0 w i > 0 ,且 1≤∑i=1nwi≤100000 1 ≤ ∑ i = 1 n w i ≤ 100000
完全想不到的神题……
花了1h+依旧停留在指数级算法上……
太菜了QAQ
思路:
考虑答案的计算式,由于是要求1号最后一个被射杀,有点难求。
于是考虑容斥,枚举在1号之后被杀的人的集合,那么设
P(s)
P
(
s
)
为这个集合的人在
1
1
号之后被杀的概率,有:
于是考虑
P(s)
P
(
s
)
怎么算。
发现这个每个时刻都在变的分母十分恶心,考虑消去它。
于是考虑改变规则为可以鞭尸,但鞭到尸就立刻再开一枪。
即有如下式子:
上式的意思是,由于不知道在射死1号之前会开几枪,于是把所有情况累加起来~
考虑将极限花去,根据无穷级数求和(或者说逆泰勒展开)易知:
于是有
于是
然而依旧不好求,复杂度与
2n
2
n
有关……
注意到条件
∑i=1nwi≤100000
∑
i
=
1
n
w
i
≤
100000
,这意味着不同的
w1+sumw(s)
w
1
+
s
u
m
w
(
s
)
数量不超过
100000
100000
个。
于是考虑将分母相同的合在一起算,做一个背包来得到每种分母的所有分子之和,然后对应累加即可~
然而背包复杂度是
O(n2)
O
(
n
2
)
的,并不能过。
于是考虑将贡献写成生成函数,将背包过程想象成多项式乘法,就可以分治FFT了。
于是就能过了。
复杂度
O(WlogWlogN)
O
(
W
log
W
log
N
)
。
#include<cstdio>
#include<algorithm>
using namespace std;
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0' || '9'<ch)ch=getchar();
while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x;
}
typedef long long ll;
const int N=5e5+9;
const int md=998244353;
inline ll qpow(ll a,ll b)
{
ll ret=1;
while(b)
{
if(b&1)ret=ret*a%md;
a=a*a%md;b>>=1;
}
return ret;
}
namespace ntt
{
int rev[N];
inline void initrev(int n)
{
for(int i=0;i<n;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)*(n>>1));
}
inline void ntt(ll *a,int n,int f)
{
for(int i=0;i<n;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
for(int h=2;h<=n;h<<=1)
{
ll w=qpow(3,(md-1)/h);
if(f)w=qpow(w,md-2);
for(int j=0;j<n;j+=h)
{
ll wn=1ll,x,y;
for(int k=j;k<j+(h>>1);k++)
{
x=a[k];y=a[k+(h>>1)]*wn%md;wn=wn*w%md;
a[k]=(x+y)%md;a[k+(h>>1)]=(x-y+md)%md;
}
}
}
if(f)
for(ll i=0,inv=qpow(n,md-2);i<n;i++)
a[i]=a[i]*inv%md;
}
inline void mul(ll *a,int n,ll *b,int m,ll *c,int k)
{
static ll d[N],e[N],l;
for(l=1;l<=n+m;l<<=1);
for(int i=0;i<n;i++)d[i]=a[i];
for(int i=0;i<m;i++)e[i]=b[i];
for(int i=n;i<l;i++)d[i]=0;
for(int i=m;i<l;i++)e[i]=0;
initrev(l);ntt(d,l,0);ntt(e,l,0);
for(int i=0;i<l;i++)
d[i]=d[i]*e[i]%md;
ntt(d,l,1);
for(int i=0;i<k && i<n+m-1;i++)
c[i]=d[i];
}
}
int n,w[N],cnt;
ll f[30][N],len[30];
inline void work(int l,int r)
{
if(l==r)
{
f[++cnt][0]=1;
f[cnt][w[l]]=md-1;
for(int i=1;i<w[l];i++)
f[cnt][i]=0;
len[cnt]=w[l]+1;
return;
}
int mid=l+r>>1;
work(l,mid);work(mid+1,r);
ntt::mul(f[cnt],len[cnt],f[cnt-1],len[cnt-1],f[cnt-1],N);
len[cnt-1]=len[cnt]+len[cnt-1]-1;cnt--;
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
w[i]=read();
work(2,n);
ll ans=0;
for(int i=0;i+w[1]<=2e5;i++)
if(f[1][i])
(ans+=qpow(w[1]+i,md-2)*f[1][i]%md)%=md;
printf("%lldn",ans*w[1]%md);
return 0;
}
最后
以上就是积极火龙果为你收集整理的[LOJ2541][PKUWC2018]猎人杀-分治FFT-容斥原理-概率与期望猎人杀的全部内容,希望文章能够帮你解决[LOJ2541][PKUWC2018]猎人杀-分治FFT-容斥原理-概率与期望猎人杀所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复