LOJ2541「PKUWC2018」猎人杀(分治+容斥原理+FFT/NTT)

传送门:https://loj.ac/problem/2541


s o l u t i o n solution solution:
直接算不好算
考虑容斥
转化一下问题,假如一个人死了之后不是真正死了,每当打到这些人后就重新开枪,这样的问题是等价的
我们枚举在1后面死的人的集合 T T T,假设 ∑ i = 1 n w i = S , ∑ i ∈ T w i = A \sum_{i=1}^n w_i=S,\sum_{i∈T} w_i =A i=1nwi=S,iTwi=A 那么此时的贡献(1死的概率) P P P的式子:

P = ∑ i = 1 ∞ ( 1 − A + w 1 S ) i ∗ w 1 S P = \sum_{i=1}^{\infty} (1-\frac{A+w_1}{S})^i*\frac{w_1}{S} P=i=1(1SA+w1)iSw1
= w 1 S ∗ ( 1 1 − 1 + A + w 1 S ) =\frac{w_1}{S}*(\frac{1}{1-1+\frac{A+w_1}{S}}) =Sw1(11+SA+w11)
= w 1 A + w 1 =\frac{w_1}{A+w_1} =A+w1w1
那么 a n s = ∑ S ( − 1 ) ∣ S ∣ w 1 s u m ( s ) + w 1 ans = \sum_{S} (-1)^{|S|}\frac{w_1}{sum(s)+w_1} ans=S(1)Ssum(s)+w1w1
对于 s u m ( S ) sum(S) sum(S)想同的不同的集合,我们可以放在一起算,问题就是怎么算容斥系数的和
可以发现 s u m ( S ) sum(S) sum(S)对应的系数就是 ∏ ( 1 − w i ) \prod(1-w_i) (1wi)的第 s u m ( S ) sum(S) sum(S)次系数
分治 N T T NTT NTT即可
复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

代码:

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long

const int g = 3;
const int p = 998244353;
const int NUM = 22;

int n , m , N;
int a[20][4001000] , b[4001000];
int s[1001000];
int w[1001000]; 
int R[4001000] , flen;
int calc(int a,int b){return ((a+b)%p+p)%p;}
int del(int a,int b){return ((a-b)%p+p)%p;}
int mul(int a,int b){return 1ll*a*b%p;}

int ksm(int a, int b)  
{  
    int ans = 1;  
    a %= p;  
    while(b) {  
        if(b & 1)    
            ans = 1ll * ans * a % p;
        b >>= 1;  
        a = 1ll * a * a % p;  
    } 
    return ans%p;  
}  
void ntt(int *a ,int f){
    for(int i = 0; i < flen; ++i)
		if(i < R[i]) swap(a[i], a[R[i]]);
    for(int len = 2;len <= flen;len *= 2){
    	int wn = ksm( g , (p - 1) / len );
    	if(f == -1) wn = ksm(wn , p-2);
    	for(int k = 0;k <= flen / len - 1;++k){
    		int st = k * len;
    		ll w = 1;
    		for(int i = 0;i < len / 2;++i){
    			int x = a[st + i] % p,
    			   y = 1ll * (a[st + i + len / 2] % p) * w % p;
    		    a[st + i] = calc(x,y);
    		    a[st + i + len / 2] = del(x,y);
    		    w = 1ll * w * wn % p;
			}
    	}
      	
    } 
    if(f == -1){
    	int inv = ksm(flen , p-2);
    	for(int i = 0;i < flen ;++i)
    	  a[i] = 1ll * a[i] * inv % p;
    }
 	return;
}	
int rd()
{
    int sum = 0;char c = getchar();bool flag = true;
    while(c < '0' || c > '9') {if(c == '-') flag = false;c = getchar();}
    while(c >= '0' && c <= '9') sum = sum * 10 + c - 48,c = getchar();
    if(flag) return sum;
    else return -sum;
}
void solve(int l,int r,int d)
{
	if(l == r)
	{
		a[d][0] = 1;a[d][w[l]] = p-1;
		rep(i,1,w[l]-1) a[d][i] = 0; 
		return;
	}
	int mid = (l+r)>>1;
	solve(l,mid,d+1);
	rep(i,0,s[mid]-s[l-1]) a[d][i] = a[d+1][i];
	solve(mid+1,r,d+1);
	flen = 1;n = s[mid] - s[l-1];m = s[r]-s[mid];
	while(flen < (n+m+1)) flen <<= 1;
	rep(i,n+1,flen-1) a[d][i] = 0;
	rep(i,m+1,flen-1) a[d+1][i] = 0;
	int L = log2(flen);
	for(int i = 0;i < flen;++i)
	{
	 	R[i] = R[i>>1]>>1;
	 	if(i & 1) R[i] |= 1<<(L-1);
    }	
    ntt(a[d],1);ntt(a[d+1],1);
	rep(i,0,flen-1) a[d][i] = mul(a[d][i],a[d+1][i]);
	ntt(a[d],-1);
}
int main()
{
	N = rd();rep(i,1,N) w[i] = rd(),s[i] = s[i-1]+w[i];
	solve(2,N,0);
	int ans = 0;
	rep(S,0,s[N]-s[1]) ans = calc(ans,mul(w[1],mul( ksm(calc(S,w[1]),p-2) , a[0][S] ) ));
	printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值