选出一个山峰一样的数列,问总长度是多少,我们发现,周长的总长只取决于最长的那一块木板和选取的模板个数,总周长就是 ( L + M ) ∗ 2 ( L + M ) * 2 (L+M)∗2
其次,我们考虑给木板按长度分类
第一类是对应长度只有一块的,那么这个木板可以放在左边,也可以放在边,对应的次数就是 a i = C ( s a , i ) ∗ 2 i a_i = C(sa,i) * 2^i ai=C(sa,i)∗2i
第二类就是对应长度有两块以上的,我们取出两块,标记一块放在左边,一块是放在右边的,那么对应的次数就是 b i = C ( s b , i ) b_i = C(sb,i) bi=C(sb,i)
因此答案就是 a i , b i a_i,b_i ai,bi 的卷积,因为两个序列可以拼起来拼成一个,那么FFT求一次卷积即可
#include
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
int Gcd(int a,int b){if (b == 0) return a; return Gcd(b , a%b);}
int Lcm(int a, int b){ return a/Gcd(a,b)*b;}
inline long long read(){
long long f = 1, x = 0;char ch = getchar();
while (ch > '9' || ch < '0'){if (ch == '-')f = -f;ch = getchar();}
while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
return x * f;
}
const int maxn = 2e6 + 10;
const double pi = acos(-1.0);
// const LL P = 2281701377;
const LL P = 998244353;
const LL mod = P;
const LL G = 3;
int n,m,Lim = 1,l,r[maxn];
LL a[maxn],b[maxn],L1[maxn],ans[maxn];
int cnt[maxn];
LL qpow(LL a,LL b){
LL base = a % P,ans = 1;
while(b){
if (b & 1) ans = ans*base % P;
base = base * base % P;
b >>= 1;
}
return ans % P;
}
inline void NTT(LL *A,int type){
for(int i=0; i; i++)
if (i < r[i]) swap(A[i],A[r[i]]);
for(int m = 1; m < Lim; m<<=1){
LL wn = qpow(G, (P-1)/(m << 1));
for(int j=0; j; j+=(m<<1)){
LL w = 1;
for(int k=0; k; k++,w = w*wn % P){
LL x = A[j+k],y = w*A[j+k+m] % P;
A[j+k] = (x + y) % P;
A[j+k+m] = (x - y + P) % P;
}
}
}
if (type == 1) return;
LL inv = qpow(Lim,P-2); reverse(a+1,a+Lim);
for(int i=0; i<=Lim; i++) a[i] = 1ll*a[i]*inv % P;
}
void init(int n,int m){
Lim = 1;
l = 0;
memset(r,0,sizeof(r));
while(Lim <= n + m) { Lim <<= 1; l++;} // 不懂,反正加上就对了
for(int i=0; i<=Lim; i++) r[i] = ( r[i>>1]>>1 )| ( (i&1)<<(l-1) );// 不懂,反正加上就对了z
}
void mul(LL* a,int n, LL* b,int m){
for(int i=n+1; i<=Lim; i++) a[i] = 0;
for(int i=m+1; i<=Lim; i++) b[i] = 0;
for(int i=0; i<=n; i++) a[i] = (a[i]%P + P) % P; // 第一个多项式系数
for(int i=0; i<=m; i++) b[i] = (b[i]%P + P) % P; // 第二个多项式系数
NTT(a,1); NTT(b,1);
for(int i=0; i<= Lim; i++) a[i] = a[i]*b[i] % P; // O(n)求点值表示法
NTT(a,-1); // 逆变换变成系数表示法
}
LL C(LL n,LL m){
static LL M = 0,inv[maxn],mul[maxn],invMul[maxn];
while(M <= n){
if (M){
inv[M] = M == 1 ? 1 : (mod-mod/M)*inv[mod%M]%mod;
mul[M] = mul[M-1]*M%mod;
invMul[M] = invMul[M-1]*inv[M] % mod;
}else{
mul[M] = 1;
invMul[M] = 1;
}
M++;
}
return mul[n]*invMul[m] % mod * invMul[n-m]%mod;
}
int main(){
int n = read(),k = read();
for(int i=1; i<=n; i++){
L1[i] = read();
}
for(int i=1; i<=k; i++){
int L = read();
memset(cnt,0,sizeof(cnt));
for(int i=1; i<=n; i++){
if (L1[i] < L){
cnt[L1[i]]++;
}
}
int sa = 0,sb = 0;
for(int i=1; i; i++){
if (cnt[i] >= 2) sb += 2;
else if (cnt[i] == 1) sa++;
}
for(int i=0; i<=sa; i++) a[i] = C(sa,i) * qpow(2,i);
for(int i=0; i<=sb; i++) b[i] = C(sb,i);
init(sa,sb);
mul(a,sa,b,sb);
for(int i=0; i<=Lim; i++){
ans[L + i + 1] += a[i];
ans[L + i + 1] %= mod;
}
}
int q = read();
while(q--){
int x = read();
printf("%lld\n",ans[x / 2]);
}
return 0;
}