题目大意
有n种球,每种球有不同的颜色,第i种球有a[i]个,现在将这些球排成一排,给出q组询问,每组询问给出一个数x,询问满足相邻的球颜色相同的个数为x的排列个数。
设m为所有球的个数和,数据满足:
1≤n,m≤2×105
题解
设g[i]表示所有球总共被分成i段的方案数(注意,g[i]所描述的每一段的颜色一定是一样的,但是相邻的段的颜色是可能一样的)
如果第i种球被分成了b[i]段,那么方案数就是:
(∑bi)!∏(bi!)
大力分治一波+ntt就好了
设f是答案
那么,将g调换有:
gi=∑j≥ifj⋅(ij)
这个式子的意义是:如果被分成了m-i段,那么相邻的球颜色相同的个数至少为i,考虑一个相邻球颜色相同个数位j的排列在其中会被计算 (ij) 次
接着有
gi⋅i!=∑j≥ifj⋅j!⋅1(j−i)!
将 gi 调换成 gm−i ,f同样
得到:
gi⋅(m−i)!=∑j≤ifj⋅(m−j)!⋅1(i−j)!
上面就是一个卷积形式了
设:
g(x)=∑i=0mgi⋅(m−i)!⋅xif(x)=∑i=0mfi⋅(m−i)!⋅xih(x)=∑i=0m1i!⋅xi
那么有:
g(x)=f(x)⋅h(x)∴f(x)=g(x)h(x)
所以就要做多项式求逆,然后直接乘就好了,注意转化过程中的颠倒的问题
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<bitset>
#include<map>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long LL;
typedef double db;
int get(){
char ch;
while(ch=getchar(),(ch<'0'||ch>'9')&&ch!='-');
if (ch=='-'){
int s=0;
while(ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-'0';
return -s;
}
int s=ch-'0';
while(ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-'0';
return s;
}
const int MAXN = 600010;
const int MAXL = 20;
const int mo = 998244353;
const int G = 3;
int n,a[MAXN];
int st[MAXN],k;
int m;
int v[MAXN];
int A[MAXN],B[MAXN];
LL ny[MAXN],js[MAXN],e[MAXN];
int g[MAXN],f[MAXN],h[MAXN],H[MAXN];
LL d[MAXN];
int bitr[MAXN];
int N,L;
int mi[23];
LL quickmi(LL x,LL tim){
LL ans=1;
for(;tim;tim/=2,x=x*x%mo)
if (tim%2)ans=ans*x%mo;
return ans;
}
void prepare(){
int v=quickmi(G,(mo-1)/N);
d[0]=1;
fo(i,1,N)d[i]=d[i-1]*v%mo;
fo(i,0,N-1){
bitr[i]=0;
fo(j,0,L-1)
if ((i&mi[j])>0)bitr[i]+=mi[L-1-j];
}
}
int add(int x,int y){
return x+y>=mo?x+y-mo:x+y;
}
void DFT(int *a){
fo(i,0,N-1)
if (i<bitr[i])swap(a[i],a[bitr[i]]);
for(int now=2;now<=N;now<<=1){
int half=now/2;
fo(i,0,half-1){
int w=d[N/now*i];
for(int j=i;j<N;j+=now){
int l=a[j],r=1ll*w*a[j+half]%mo;
a[j]=add(l,r);
a[j+half]=add(l,(mo-r)%mo);
}
}
}
}
void IDFT(int *a){
fo(i,0,N-1)
if (i<bitr[i])swap(a[i],a[bitr[i]]);
for(int now=2;now<=N;now<<=1){
int half=now/2;
fo(i,0,half-1){
int w=d[N-N/now*i];
for(int j=i;j<N;j+=now){
int l=a[j],r=1ll*w*a[j+half]%mo;
a[j]=add(l,r);
a[j+half]=add(l,(mo-r)%mo);
}
}
}
LL tmp=quickmi(N,mo-2);
fo(i,0,N-1)a[i]=tmp*a[i]%mo;
}
void get_nv(int *f,int *f0,int len){
if (len==1){
f0[0]=quickmi(f[0],mo-2);
return;
}
get_nv(f,f0,(len+1)/2);
N=1;L=0;
while(N<=2*len){N<<=1;L++;}
prepare();
fo(i,0,N-1)A[i]=B[i]=0;
fo(i,0,(len+1)/2-1)B[i]=f0[i];
fo(i,0,len-1)A[i]=f[i];
DFT(A);
DFT(B);
fo(i,0,N-1)A[i]=1ll*B[i]*(2ll+mo-1ll*A[i]*B[i]%mo)%mo;
IDFT(A);
fo(i,0,len-1)f0[i]=A[i];
}
int main(){
freopen("color.in","r",stdin);
freopen("color.out","w",stdout);
mi[0]=1;
fo(i,1,20)mi[i]=mi[i-1]<<1;
n=get();
js[0]=ny[0]=1;
fo(i,1,200000)js[i]=js[i-1]*i%mo;
ny[200000]=quickmi(js[200000],mo-2);
fd(i,199999,1)ny[i]=ny[i+1]*(i+1)%mo;
fo(i,1,200000)e[i]=ny[i]*js[i-1]%mo;
fo(i,1,n){
st[++k]=m+1;
m+=(a[i]=get())+1;
LL now=1;
fo(j,0,a[i]-1){
v[st[k]+j+1]=now*ny[j+1]%mo;
now=now*e[j+1]%mo*(a[i]-1-j)%mo;
}
}
for(N=1,L=0;k>1;N<<=1,L++){
prepare();
int k_=k;
k=0;
for(int now=1,w=1;now<=k_;now=w+1){
if ((now<k_&&a[now]+a[now+1]>=N)||(now==k_)){
st[++k]=st[now];
a[k]=a[now];
w=now;
continue;
}
w=now+1;
fo(i,0,a[now])A[i]=v[st[now]+i];
fo(i,0,a[w])B[i]=v[st[w]+i];
DFT(A);
DFT(B);
fo(i,0,N-1)A[i]=1ll*A[i]*B[i]%mo;
IDFT(A);
fo(i,0,a[now]+a[w])v[st[now]+i]=A[i];
fo(i,0,N-1)A[i]=B[i]=0;
st[++k]=st[now];
a[k]=a[now]+a[w];
}
}
m-=n;
fo(i,0,m)g[i]=1ll*v[i+1]*js[m-i]%mo*js[i]%mo;
fo(i,0,m)h[i]=ny[i];
get_nv(h,H,m+1);
if (N<=2*m){
N<<=1;L++;
prepare();
}
DFT(g);
DFT(H);
fo(i,0,N-1)f[i]=1ll*g[i]*H[i]%mo;
IDFT(f);
fo(i,0,m)f[i]=1ll*f[i]*ny[m-i]%mo;
fo(i,0,m/2)swap(f[i],f[m-i]);
for(int q=get();q;q--){
int x=get();
if (x>m)printf("0\n");
else printf("%d\n",f[x]);
}
fclose(stdin);
fclose(stdout);
return 0;
}