ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[PKUWC2018] 猎人杀

2021-04-07 19:35:30  阅读:214  来源: 互联网

标签:frac int sum 容斥 tot 猎人 PKUWC2018 MOD


一、题目

听说过这题很久了,这么经典怎么能不做呢?

点此看题

二、解法

由于概率一直在变算着麻烦得很,有一个神奇 \(\tt idea\) 就是我们乱开枪,如果这一枪在鞭尸那么就再开一枪,知道打死第一个人为止。这种策略的证明也不难,对于一个人被打到的概率都只和 \(w_i\) 的比值有关

然后还是不会做,一个想都不敢想的方法是指数级容斥,枚举集合 \(S\) 表示至少集合 \(S\) 中的人在 \(1\) 的后面死掉,那么答案是:

这一步妙极了,我们要主动使用容斥而不是看着像容斥才用(和 \(\tt cdq\) 差不多)而不是看着很像再用容斥,有很多方法能把容斥的复杂度降下来,常见的就是 \(dp\) 维护容斥系数。

\[\sum_{S}(-1)^{|S|}P(S) \]

其中 \(P(S)\) 表示至少集合 \(S\) 中的人在 \(1\) 后面死的概率,那么可以打无限枪,只要不打到 \(1\) 和 \(S\) 中的人就可以,然后我们再给 \(1\) 补一枪,这样可以写出 \(P(S)\) 的表达式了:

\[P(S)=\sum_{i=0}^\infty(\frac{tot-a[1]-sum[S]}{tot})^i\times\frac{a[1]}{tot} \]

直接把他当母函数写闭形式:

\[P(S)=\frac{1}{1-\frac{tot-a[1]-sum[S]}{tot}}\times\frac{a[1]}{tot}=\frac{a[1]}{a[1]+sum[S]} \]

发现就只有 \(sum[S]\) 需要算一算了,发现这就是一个背包问题,很容易 \(O(n^2)\) 解决啊。

再回去看一眼题目,\(\sum a_i\leq 10^5\),也就是总容量是很小的,那么不难想到用生成函数优化背包。有 \(n-1\) 个形如 \(1-x^{a}\) 这样的函数我们要求他的卷积,那么直接分治就可以了。

注意分治的时候要按容量对半分,网上的题解大多是从中间分一半,我觉得这样是错的,但是我不是特别清楚怎么卡额。一定要这样才能说明复杂度是 \(O(n\log^2 n)\)

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 200005;
const int MOD = 998244353;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,ans,w[M],s[M],f[M];
namespace poly
{
	int len,A[M],B[M],rev[M];
	int qkpow(int a,int b)
	{
		int r=1;
		while(b>0)
		{
			if(b&1) r=r*a%MOD;
			a=a*a%MOD;
			b>>=1;
		}
		return r;
	}
	void NTT(int *a,int len,int op)
	{
		for(int i=0;i<len;i++)
		{
			rev[i]=(rev[i>>1]>>1)|((len/2)*(i&1));
			if(i<rev[i]) swap(a[i],a[rev[i]]);
		}
		for(int s=2;s<=len;s<<=1)
		{
			int t=s/2,w=(op==1)?qkpow(3,(MOD-1)/s):qkpow(3,MOD-1-(MOD-1)/s);
			for(int i=0;i<len;i+=s)
				for(int j=0,x=1;j<t;j++,x=x*w%MOD)
				{
					int fe=a[i+j],fo=a[i+j+t];
					a[i+j]=(fe+x*fo)%MOD;
					a[i+j+t]=((fe-x*fo)%MOD+MOD)%MOD;
				}
		}
		if(op==1) return ;
		int inv=qkpow(len,MOD-2);
		for(int i=0;i<len;i++) a[i]=a[i]*inv%MOD;
	}
	void work(int n,int m,int *a,int *b,int *c)
	{
		len=1;while(len<n+m) len<<=1;
		for(int i=0;i<len;i++) A[i]=B[i]=0;
		for(int i=0;i<n;i++) A[i]=a[i];
		for(int i=0;i<m;i++) B[i]=b[i];
		NTT(A,len,1);NTT(B,len,1);
		for(int i=0;i<len;i++) A[i]=A[i]*B[i]%MOD;
		NTT(A,len,-1);
		for(int i=0;i<n+m;i++)
			c[i]=A[i];
	}
}
void cdq(int *a,int l,int r)
{
	if(l==r)
	{
		a[0]=1;a[w[l]]=MOD-1;
		return ;
	}
	int mid=l;
	while(mid<r-1 && 2*(s[mid]-s[l-1])<s[r]-s[l-1]) mid++;
	int l1=s[mid]-s[l-1],l2=s[r]-s[mid];
	int f[2*l1]={},g[2*l2]={};
	cdq(f,l,mid);cdq(g,mid+1,r);
	poly::work(l1+1,l2+1,f,g,a);
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		w[i]=read();
		s[i]=s[i-1]+w[i];
	}
	cdq(f,2,n);
	for(int i=0;i<=s[n];i++)
	{
		int tmp=w[1]*poly::qkpow(w[1]+i,MOD-2)%MOD;
		ans=(ans+tmp*f[i])%MOD;
	}
	printf("%lld\n",ans);
}

标签:frac,int,sum,容斥,tot,猎人,PKUWC2018,MOD
来源: https://www.cnblogs.com/C202044zxy/p/14629132.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有