题面
题解
这个值域可以告诉我们很多信息,比如质数个数,即,平方在值域以内的质数最多 14 个。
这提示我们,每个数质因数分解后不属于前 14 个质数的质因数最多一个。
而且,这个范围我们可以再压缩一下,由于 43 × 47 > 2000 43\times47>2000 43×47>2000 ,所以每个数不属于前 13 个质数的质因数最多一种,即要么只有一个,要么是 4 3 2 43^2 432 ,只有一种。
于是我们可以把前 13 个质数是否存在的状态进行压缩,然后做一个类似背包DP的算法。怎么满足 13 个以后的质数呢?我们可以按照最大的质因数分组,当出现 13 个以后的质数 P P P 存在的条件时,就把每个状态的总方案数(考虑进 13 个以后的所有质数的方案数)除去【 P P P 组任选方案数】,再乘上【 P P P 组至少选一个的方案数】。由于【 P P P 组任选方案数】是 2 的幂,所以一定存在逆元。
这个特殊背包DP的转移,可以用 FWT 卷积优化。具体地,我们可以先把方案数的DP数组进行正变换预处理,然后查询的时候线性修改,最后一次逆变换求答案。
时间复杂度 O ( 2 13 ∑ c i + m ⋅ 2 13 ⋅ 13 ) O(2^{13}\sum c_i+m\cdot2^{13}\cdot13) O(213∑ci+m⋅213⋅13) 。
CODE
#include<set>
#include<map>
#include<queue>
#include<cmath>
#include<vector>
#include<random>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 2005
#define LL long long
#define DB double
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
inline int xchar() {
static const int maxnn = 1000000;
static char b[maxnn];
static int pos = 0,len = 0;
if(pos == len) pos = 0,len = fread(b,1,maxnn,stdin);
if(pos == len) return -1;
return b[pos ++];
}
//#define getchar() xchar()
inline LL read() {
LL f=1,x=0;int s = getchar();
while(s<'0'||s>'9') {if(s<0)return -1;if(s=='-')f=-f;s=getchar();}
while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s^48);s = getchar();}
return f * x;
}
void putpos(LL x) {
if(!x) return ;
putpos(x/10); putchar((x%10)^48);
}
void putnum(LL x) {
if(!x) {putchar('0');return ;}
if(x < 0) putchar('-'),x = -x;
return putpos(x);
}
inline void AIput(LL x,int c) {putnum(x);putchar(c);}
const int MOD = 998244353;
const int iv2 = (MOD+1)/2;
int n,m,s,o,k;
int pw[1000005],ip[1000005];
int p[310],f[MAXN],cnt,id[MAXN];
void sieve(int n) {
for(int i = 2;i <= n;i ++) {
if(!f[i]) p[++ cnt] = i,id[i] = cnt;
for(int j = 1;j <= cnt && i*p[j] <= n;j ++) {
f[i*p[j]] = 1; if(i % p[j] == 0) break;
}
}return ;
}
int ct[MAXN];
int s0[310][1<<13|5],s1[310][1<<13|5];
inline void MD(int &x) {if(x>=MOD)x-=MOD;}
void DWTOR(int *s,int n) {
for(int k = 2;k <= n;k <<= 1) {
for(int j = 0;j < n;j += k) {
for(int i = j;i < j+(k>>1);i ++) {
MD(s[i+(k>>1)] += s[i]);
}
}
} return ;
}
void IDWTOR(int *s,int n) {
for(int k = n;k > 1;k >>= 1) {
for(int j = 0;j < n;j += k) {
for(int i = j;i < j+(k>>1);i ++) {
MD(s[i+(k>>1)] += MOD-s[i]);
}
}
} return ;
}
int tmp[1<<13|5];
int A[1<<13|5];
int main() {
// freopen(...
sieve(2000);
n = read();
pw[0] = 1; ip[0] = 1;
for(int i = 1;i <= n;i ++) {
MD(pw[i] = (pw[i-1]<<1));
ip[i] = ip[i-1] *1ll* iv2 % MOD;
ct[read()] ++;
}
int tp = 1<<13;
for(int i = 0;i < tp;i ++) tmp[i] = 1;
for(int i = 0;i <= cnt;i ++) {
for(int j = 0;j < tp;j ++) s0[i][j] = 1,s1[i][j] = 1;
}
for(int i = 1;i <= 2000;i ++) {
int nn = i;
int ls = 0,S = 0;
for(int j = 1;j <= cnt && p[j] <= nn;j ++) {
if(nn % p[j] == 0) {
if(j <= 13) S |= (1<<(j-1));
ls = j; while(nn % p[j] == 0) nn /= p[j];
}
}
int nm = pw[ct[i]],nm2 = ip[ct[i]];
for(int j = 0;j < tp;j ++) {
if((j&S) == S) {
s0[ls][j] = s0[ls][j] *1ll* nm2 % MOD;
s1[ls][j] = s1[ls][j] *1ll* nm % MOD;
}
}
}
for(int i = 0;i <= cnt;i ++) {
for(int j = 0;j < tp;j ++) {
tmp[j] = tmp[j] *1ll* s1[i][j] % MOD;
MD(s1[i][j] += MOD-1);
}
}
m = read();
for(int i = 1;i <= m;i ++) {
k = read();
for(int j = 0;j < tp;j ++) A[j] = tmp[j];
for(int j = 1;j <= cnt;j ++) f[j] = 0;
int S = 0;
while(k --) {
int x = id[read()];
if(x <= 13) S |= (1<<(x-1));
else f[x] = 1;
}
for(int j = 14;j <= cnt;j ++) {
if(f[j]) {
for(int k = 0;k < tp;k ++) {
A[k] = A[k] *1ll* s0[j][k] % MOD * s1[j][k] % MOD;
}
}
}
IDWTOR(A,tp);
int ans = 0;
for(int j = 0;j < tp;j ++) {
if((j&S) == S) {
MD(ans += A[j]);
}
}
AIput(ans,'\n');
}
return 0;
}