正题
这个游戏整整玩了我三天。
题意就是要你求。还要你求
。那我们暂且先不理会nm这玩意儿。
利用二项式定理,化简一下可以得到:
看到了喜闻乐见的卷积形式,但是我们难以求出可用于卷积的两个数组。
这个其实可以用生成函数来解决,在我的博客中就以这个模型来介绍生成函数,大家可以看看。
所以我们最后再用这两个函数做一遍卷积,乘上k!后除以nm输出即可。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
const int maxm=1e5*6+10;
int n,m,t;
long long A[maxm],B[maxm];
long long f[maxm],g[maxm],p[maxm],q[maxm];
long long inv[maxm],fac[maxm];
int limit,l,where[maxm];
const long long mod=998244353;
long long ksm(long long x,long long t){
long long tot=1;
while(t){
if(t&1) (tot*=x)%=mod;
(x*=x)%=mod;
t/=2;
}
return tot;
}
void ntt(long long *now,int idft){
for(int i=0;i<limit;i++) if(i<where[i]) swap(now[i],now[where[i]]);
long long wn,w,a,b;
for(int l=2;l<=limit;l*=2){
wn=ksm(3,(mod-1)/l);
if(idft==-1) wn=ksm(wn,mod-2);
for(int i=0;i<limit;i+=l){
w=1;
for(int x=i,y=i+l/2;y<i+l;x++,y++,(w*=wn)%=mod){
a=now[x],b=now[y]*w%mod;
now[x]=(a+b)%mod;
now[y]=(a+mod-b)%mod;
}
}
}
}
void multi(long long *now,int a,int b,int c,int d){
limit=1,l=0;
while(limit<d-a) limit*=2,l++;
for(int i=0;i<limit;i++) where[i]=((where[i>>1]>>1)|((i&1)<<(l-1)));
for(int i=0;i<=b-a;i++) f[i]=now[a+i];
for(int i=0;i<=d-c;i++) g[i]=now[c+i];
ntt(f,1);ntt(g,1);
for(int i=0;i<limit;i++) (f[i]*=g[i])%=mod;ntt(f,-1);
long long inv=ksm(limit,mod-2);
for(int i=0;i<=d-a;i++) now[i+a]=f[i]*inv%mod;
for(int i=0;i<limit;i++) f[i]=g[i]=0;
}
void dfs(long long *now,int l,int r){
if(l==r) return ;
int mid=(l+r)/2;
dfs(now,l,mid);
dfs(now,mid+1,r);
multi(now,l*2,mid*2+1,(mid+1)*2,r*2+1);
}
void get_inv(long long *inv,long long *now,int n){
if(n==1) {inv[0]=ksm(now[0],mod-2);return ;}
get_inv(inv,now,(n+1)/2);
for(int i=0;i<n;i++) p[i]=inv[i]*2%mod;
limit=1,l=0;
while(limit<3*n-2) limit*=2,l++;
for(int i=0;i<limit;i++) where[i]=((where[i>>1]>>1)|((i&1)<<(l-1))),q[i]=0;
for(int i=0;i<n;i++) q[i]=now[i];
for(int i=n;i<limit;i++) q[i]=0;
ntt(inv,1);ntt(q,1);
for(int i=0;i<limit;i++) inv[i]=inv[i]*inv[i]%mod*q[i]%mod;ntt(inv,-1);
long long op=ksm(limit,mod-2);
for(int i=0;i<n;i++) inv[i]=(p[i]+mod-inv[i]*op%mod)%mod;
for(int i=n;i<limit;i++) inv[i]=0;
}
void mul(long long *a,long long *b,int l1,int l2){
limit=1,l=0;
while(limit<l1+l2-1) limit*=2,l++;
for(int i=l1;i<limit;i++) a[i]=0;
for(int i=l2;i<limit;i++) b[i]=0;
for(int i=0;i<limit;i++) where[i]=((where[i>>1]>>1)|((i&1)<<(l-1)));
ntt(a,1);ntt(b,1);
for(int i=0;i<limit;i++) (a[i]*=b[i])%=mod;ntt(a,-1);
long long inv=ksm(limit,mod-2);
for(int i=0;i<limit;i++) (a[i]*=inv)%=mod;
}
int op;
void print(){
for(int i=0;i<=op;i++) printf("%lld ",A[i]);printf("\n");
for(int i=0;i<=op;i++) printf("%lld ",B[i]);printf("\n");
}
int main(){
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++) scanf("%lld",&A[i*2+1]),A[i*2]=1,A[i*2+1]=(mod-A[i*2+1])%mod;
for(int i=0;i<m;i++) scanf("%lld",&B[i*2+1]),B[i*2]=1,B[i*2+1]=(mod-B[i*2+1])%mod;
scanf("%d",&t);op=max(n,max(m,t));
dfs(A,0,n-1);dfs(B,0,m-1);
fac[0]=1;for(int i=1;i<=op;i++) fac[i]=(fac[i-1]*i)%mod;
inv[op]=ksm(fac[op],mod-2);for(int i=op-1;i>=0;i--) inv[i]=(inv[i+1]*(i+1))%mod;
get_inv(f,A,op+1);get_inv(g,B,op+1);
for(int i=0;i<=n;i++) A[i]=A[i+1]*(i+1)%mod;
for(int i=0;i<=m;i++) B[i]=B[i+1]*(i+1)%mod;
mul(A,f,n,op+1);mul(B,g,m,op+1);
for(int i=op;i>=1;i--) A[i]=(mod-A[i-1])%mod*inv[i]%mod;A[0]=n;
for(int i=op;i>=1;i--) B[i]=(mod-B[i-1])%mod*inv[i]%mod;B[0]=m;
mul(A,B,op+1,op+1);
long long temp=ksm((long long)n*m%mod,mod-2);
for(int i=1;i<=t;i++) printf("%lld\n",A[i]*fac[i]%mod*temp%mod);
}

本文探讨了一个游戏算法问题,通过使用二项式定理和生成函数,将复杂的问题转化为卷积形式,实现了算法的高效求解。文章详细介绍了如何利用生成函数和快速傅立叶变换(FFT)进行数组卷积,最终解决了原问题。
281

被折叠的 条评论
为什么被折叠?



