zoukankan      html  css  js  c++  java
  • 洛谷 P5644

    题面传送门

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

    首先注意到每次开枪打死一个猎人之后,打死其他猎人概率的分母就会发生变化,这将使我们维护起来非常棘手,因此我们考虑做一个转化:每次随便从全集中选出一个猎人(不管死的活的),如果它是活的就将它射死。假设现在死了的猎人的 (w_i) 值之和为 (T),所有猎人的 (w_i) 值之和为 (U),那么精通无穷级数的同学应该不难推出,对于某个还活着的猎人 (j),射到的第一个活着的猎人是 (j) 的概率就是 (sumlimits_{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 otin S)) 并令 (S) 中的猎人必须在 (1) 之后死,我们记这样的概率为 (p(S)),那么答案显然就是 (sumlimits_{1 otin S}p(S)(-1)^{|S|})。考虑这个 (p(S)) 是个什么东西,按照上面的转化,(S) 中的猎人在 (1) 之后死即意味着在打死 (1) 之前选择的猎人都不在 (S) 中,那么我们可以枚举打死 (1) 之前开了多少枪,设这个数是 (c),方便起见我们假设 (X=sumlimits_{xin S}w_x),那么可列出方程 (p(S)=sumlimits_{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=sumlimits_{X}dfrac{w_1}{X+w_1}sumlimits_{S}(-1)^{|S|}[sumlimits_{xin S}w_x=X]),也就是说如果我们能求出所有满足 (sumlimits_{xin S}w_x=X)((-1)^{|S|}) 之和那这题就搞定了。这东西怎么求呢?这东西看起来好像有点眼熟,(w_x) 之和等于 (X) 可以看作……系数之和等于 (X),对!生成函数(想不到 *2,u1s1 中考结束后 wtm 简直像个 sb)。我们令 (F(x)=prodlimits_{i=2}^n(1-x^{w_i})),那么这东西就是 ([x^{X}]F(x)),由于 (sumlimits_{i=1}^nw_ile 10^5),因此可以分治+NTT(为什么是“分治+NTT”而不是“分治 NTT”呢?因为这里的分治不是 cdq 分治)求出 (F(x)),时间复杂度 (nlog^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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Hbase JavaApi
    面向对象特征之继承
    重写(Override)与重载(Overload)
    数组排序
    EL表达式
    java异常
    业务代码与非业务代码
    设计思想之高内聚低耦合
    JDBC实现动态查询
    枚举
  • 原文地址:https://www.cnblogs.com/ET2006/p/luogu-P5644.html
Copyright © 2011-2022 走看看