20200727 T2 小w玩游戏【生成函数(二项式反演技巧)】

题目描述

在这里插入图片描述在这里插入图片描述

题目分析

设有 i i i 行选了奇数次的方案数为 f i f_i fi j j j 列选了奇数次的方案数为 g j g_j gj

A n s = ∑ i m + j n − 2 i j ≤ k f i ∗ g j Ans=\sum_{im+jn-2ij\le k} f_i*g_j Ans=im+jn2ijkfigj

f i = q ! [ x q ] ( e x − e − x 2 ) i ( e x + e − x 2 ) n − i ( n i ) f_i=q![x^q](\frac {e^x-e^{-x}}2)^i(\frac {e^x+e^{-x}}2)^{n-i}\binom ni fi=q![xq](2exex)i(2ex+ex)ni(in)

直接推不太好推(但是是可行的,有个小技巧,下面会讲到)

如果只有一个有幂会舒服一点。考虑二项式反演, h i h_i hi 为强制有 i i i 行奇数,其余任意的方案。

h i = q ! [ x q ] ( e x − e − x 2 ) i e ( n − i ) x ( n i ) = ( 1 2 ) i ( n i ) q ! [ x q ] ∑ j = 0 i ( i j ) ( − 1 ) i − j e x ( n − 2 i + 2 j ) h_i=q![x^q](\frac {e^x-e^{-x}}2)^ie^{(n-i)x}\binom ni\\ =(\frac 12)^i\binom niq![x^q]\sum_{j=0}^i\binom ij(-1)^{i-j}e^{x(n-2i+2j)} hi=q![xq](2exex)ie(ni)x(in)=(21)i(in)q![xq]j=0i(ji)(1)ijex(n2i+2j)
q ! [ x q ] e x ( n − 2 i + 2 j ) = ( n − 2 i + 2 j ) q q![x^q]e^{x(n-2i+2j)}=(n-2i+2j)^q q![xq]ex(n2i+2j)=(n2i+2j)q

然后就可以卷积了。

二项式反演还要再卷一次。

算完 f , g f,g f,g 后枚举 i i i j ( n − 2 i ) ≤ k − i m j(n-2i)\le k-im j(n2i)kim,根据 n − 2 i n-2i n2i 的正负求对应的前缀和即可。

注意负数除法下取整是变大的。

然后说一说怎么不用二项式反演直接推式子。

对于那两个很像的底数,把一个用另一个的形式表示,即:
q ! [ x q ] ( n i ) ( 1 2 ) n ( e x − e − x ) i ( e x + e − x ) n − i = q ! [ x q ] ( n i ) ( 1 2 ) n ( e x + e − x − 2 e − x ) i ( e x + e − x ) n − i = q ! [ x q ] ( n i ) ( 1 2 ) n ∑ j = 0 i ( − 2 ) i − j ( e x + e − x ) n − i + j e − x ( i − j ) = q ! [ x q ] ( n i ) ( 1 2 ) n ∑ j = 0 i ( − 2 ) i − j ∑ k = 0 n − i + j ( n − i + j k ) e ( 2 k − n ) x = ( n i ) ( 1 2 ) n ∑ j = 0 i ( − 2 ) i − j ∑ k = 0 n − i + j ( n − i + j k ) ( 2 k − n ) q q![x^q]\binom ni(\frac 12)^n(e^x-e^{-x})^i(e^x+e^{-x})^{n-i}\\ =q![x^q]\binom ni(\frac 12)^n(e^x+e^{-x}-2e^{-x})^i(e^x+e^{-x})^{n-i}\\ =q![x^q]\binom ni(\frac 12)^n\sum_{j=0}^i(-2)^{i-j}(e^x+e^{-x})^{n-i+j}e^{-x(i-j)}\\ =q![x^q]\binom ni(\frac 12)^n\sum_{j=0}^i(-2)^{i-j}\sum_{k=0}^{n-i+j}\binom {n-i+j}ke^{(2k-n)x}\\ =\binom ni(\frac 12)^n\sum_{j=0}^i(-2)^{i-j}\sum_{k=0}^{n-i+j}\binom {n-i+j}k(2k-n)^q q![xq](in)(21)n(exex)i(ex+ex)ni=q![xq](in)(21)n(ex+ex2ex)i(ex+ex)ni=q![xq](in)(21)nj=0i(2)ij(ex+ex)ni+jex(ij)=q![xq](in)(21)nj=0i(2)ijk=0ni+j(kni+j)e(2kn)x=(in)(21)nj=0i(2)ijk=0ni+j(kni+j)(2kn)q
第二个 ∑ \sum 可以看作 G ( i − j ) G(i-j) G(ij),一次卷积可以求出 G G G,然后再卷一次就可以求出 f f f 了。

Code(二项式反演):

#include<bits/stdc++.h>
#define maxn 550005
using namespace std;
const int mod = 998244353, inv2 = (mod+1)/2;
int w[maxn],r[maxn],WL,fac[maxn],inv[maxn];
int Pow(int a,int b){int s=1;for(;b;b>>=1,a=1ll*a*a%mod) b&1&&(s=1ll*s*a%mod);return s;}
int C(int n,int m){return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
void init(int n){
	w[0]=WL=1; while(WL<=n) WL<<=1; w[1]=Pow(3,(mod-1)/WL);
	for(int i=2;i<=WL;i++) w[i]=1ll*w[i-1]*w[1]%mod;
	fac[0]=fac[1]=inv[0]=inv[1]=1;
	for(int i=2;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int i=2;i<=n;i++) inv[i]=1ll*inv[i-1]*inv[i]%mod;
}
int upd(int x){return x+(x>>31&mod);}
void NTT(int *a,int len,int flg){
	for(int i=0;i<len;i++) if(i<(r[i]=r[i>>1]>>1|(i&1?len>>1:0))) swap(a[i],a[r[i]]);
	for(int i=2,l=1,v;i<=len;l=i,i<<=1)
		for(int j=0,t=WL/i;j<len;j+=i)
			for(int k=j,o=0;k<j+l;k++,o+=t)
				a[k+l]=upd(a[k]-(v=1ll*w[flg^1?WL-o:o]*a[k+l]%mod)),a[k]=upd(a[k]+v-mod);
	if(flg^1) for(int i=0,Inv=mod-(mod-1)/len;i<len;i++) a[i]=1ll*a[i]*Inv%mod;
}
int n,m,qq,A[maxn],B[maxn],f[maxn],g[maxn],h[maxn];
long long q,k,lim;
void calc(int n,int *f){
	init(2*n);
	memset(A,0,sizeof A),memset(B,0,sizeof B);
	for(int i=0;i<=n;i++) A[i]=inv[i],B[i]=1ll*(i&1?mod-1:1)*inv[i]%mod*Pow(upd(n-2*i),qq)%mod;
	NTT(A,WL,1),NTT(B,WL,1);
	for(int i=0;i<WL;i++) A[i]=1ll*A[i]*B[i]%mod;
	NTT(A,WL,-1); memset(h,0,sizeof h);
	for(int i=0,pw=1;i<=n;i++,pw=1ll*pw*inv[2]%mod) h[i]=1ll*A[i]*fac[n]%mod*inv[n-i]%mod*pw%mod;
	memset(A,0,sizeof A),memset(B,0,sizeof B);
	for(int i=0;i<=n;i++) A[i]=1ll*h[i]*fac[i]%mod,B[i]=1ll*inv[n-i]*(n-i&1?mod-1:1)%mod;
	NTT(A,WL,1),NTT(B,WL,1);
	for(int i=0;i<WL;i++) A[i]=1ll*A[i]*B[i]%mod;
	NTT(A,WL,-1);
	for(int i=0;i<=n;i++) f[i]=1ll*A[n+i]*inv[i]%mod;
}
int main()
{
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	scanf("%d%d%lld%lld",&n,&m,&q,&k),qq=q%(mod-1);
	if(q==0) return puts("1"),0;
	calc(n,f),calc(m,g);
	for(int i=1;i<=m;i++) g[i]=(g[i]+g[i-1])%mod;
	int ans=0;
	for(int i=0;i<=n;i++)
		if(n>2*i){
			if(k>=1ll*i*m) ans=(ans+1ll*f[i]*g[min((k-1ll*i*m)/(n-2*i),1ll*m)])%mod;
		}
		else if(n<2*i){
			if((lim=(1ll*i*m-k+2*i-n-1)/(2*i-n))<=m) ans=(ans+1ll*f[i]*(g[m]-(lim>0?g[lim-1]:0)))%mod;
		}
		else if(k-1ll*i*m>=0) ans=(ans+1ll*f[i]*g[m])%mod;
	printf("%d\n",(ans+mod)%mod);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值