题目
思路
其实只要 对于每个质因数,次数搞定了就行。
题外话:这个转化并不容易想到吧……我想到它完全是因为这是我们的 F W T \tt FWT FWT 专题中的题目。
用四进制数
S
S
S 表示,
gcd
\gcd
gcd 搞定否和
l
c
m
\rm lcm
lcm 搞定否。则
f
i
(
S
)
=
f
i
−
1
(
S
)
+
f
i
−
1
(
S
∪
g
i
)
f_i(S)=f_{i-1}(S)+f_{i-1}(S\cup g_i)
fi(S)=fi−1(S)+fi−1(S∪gi)
这里用
g
i
g_i
gi 表示
i
i
i 可以满足哪些条件。虽说有的数字是绝对不能选的……
先别管其他的事儿了。这里 S S S 的范围太大了 ! ! ! !!! !!! 怎么办?
假如 gcd \gcd gcd 和 l c m \rm lcm lcm 指数相同,那么可以去掉,因为没得选。
假如
gcd
\gcd
gcd 和
l
c
m
\rm lcm
lcm 指数差
1
1
1 ,这倒也可以。那么
l
c
m
\rm lcm
lcm 至少就是
gcd
\gcd
gcd 的
p
p
p 倍。取
gcd
=
1
\gcd=1
gcd=1 也最多只有
8
8
8 个
p
p
p ,因为
2
×
3
×
5
×
7
×
11
×
13
×
17
×
19
×
23
=
223
,
092
,
870
2\times 3\times 5\times 7\times 11\times 13\times 17\times 19\times 23=223,092,870
2×3×5×7×11×13×17×19×23=223,092,870
已经超过了
1
0
8
10^8
108 的范围限制啦!于是
S
S
S 被压缩到了
2
16
2^{16}
216 这么大!
此时 n n n 个数字并不是很重要,它们可以根据效用分成 S + 1 S+1 S+1 类(其中一类是不能选的有害垃圾)。具体而言——枚举 L L L 的因数就可以啦!复杂度仅仅 O ( 8 L log L ) \mathcal O(8\sqrt{L}\log L) O(8LlogL) 而已!
不妨设每一类的个数为 a i a_i ai ,那么我们只需要选几个类,使得它们的按位或为 t t t ,此时方案数为 ∏ ( 2 a i − 1 ) \prod (2^{a_i}-1) ∏(2ai−1) ,就是选这个类中的若干个嘛。
那么我们用分治?欲求出 v ∈ [ 0 , 2 n ) v\in [0,2^n) v∈[0,2n) 的方案数,只需求出 v ∈ [ 0 , 2 n − 1 ) v\in[0,2^{n-1}) v∈[0,2n−1) 能凑出啥, v ∈ [ 2 n − 1 , 2 n ) v\in[2^{n-1},2^{n}) v∈[2n−1,2n) 能凑出啥,然后拼起来。
不难发现递归时最高位被确定了!所以问题规模确实在缩小。合并可以用
F
W
T
\tt FWT
FWT 作按位或卷积。所以复杂度是
T
(
n
)
=
2
T
(
n
2
)
+
O
(
n
log
n
)
=
O
(
n
log
2
n
)
T(n)=2T\left({n\over 2}\right)+\mathcal O(n\log n)=\mathcal O(n\log^2n)
T(n)=2T(2n)+O(nlogn)=O(nlog2n)
其中 n n n 最大为 2 16 2^{16} 216 ,还挺快的。但是跑偏了……
回到问题上:必须选 X X X 怎么满足?考虑找到 X X X 所在的类别(效用等价类),然后强迫不选这个类别。这是那个经典问题了,跟这道题需要搞定的问题是一样的。也用分治?如果单点加入是直接加入的话,每次加入就高达 S S S 的复杂度,恐怕不行。
那么区间加入必须一起加入。考虑把每个分治时得到的,类似线段树区间的,凑出每种状态的方案数 ⟨ b ⟩ \langle b\rangle ⟨b⟩ 都存下来?空间大小为 O ( S log S ) \mathcal O(S\log S) O(SlogS) 。然后可以硬性 O ( N log N ) \mathcal O(N\log N) O(NlogN) 吗?显然不是的!因为上面的更长,需要将就上面的!
能不能把上面的削短?因为我们想要的只有全集。譬如,这一步我往右走,说明这一位选 0 0 0 或 1 1 1 并不重要——必须选的这一位是 1 1 1 呢!往左,则相反,这里必须选 1 1 1 。所以我们可以把这一位砍掉!
此时,复杂度应当为
T
(
N
)
=
2
T
(
N
2
)
+
O
(
N
log
N
)
=
O
(
N
log
2
N
)
T(N)=2T\left({N\over 2}\right)+\mathcal O(N\log N)=\mathcal O(N\log^2N)
T(N)=2T(2N)+O(NlogN)=O(Nlog2N)
啊哈!搞定了!不过你大概也猜的到代码有多难打。
虽说常数可能不会太优秀(因为有很多复制、乘法等操作),但是并不慢。
代码
由于每个区间存储的 ⟨ b ⟩ \langle b\rangle ⟨b⟩ 是残缺的(省略了前面几位)并且没有考虑区间内一个不选的情况,要特别思考一下这东西。 F W T \tt FWT FWT 做完后,有可能需要再加一份乘数。
而 s o l v e solve solve 中的 b b b 可能不需要加。因为 b b b 实际上代表的是前面有一些 0 0 0 的状态,不能加入。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int_;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int Mod = 1e9+7;
const int inv2 = (Mod+1)>>1;
inline int qkpow(int_ b,int q){
int a = 1;
for(; q; q>>=1,b=b*b%Mod)
if(q&1) a = a*b%Mod;
return a;
}
void FWT(int a[],int n,int opt){
for(int i=0; i<n; ++i)
for(int j=0; j<(1<<n); ++j)
if(j>>i&1){
a[j] = (a[j]+opt*a[j^(1<<i)])%Mod;
if(a[j] < 0) a[j] += Mod;
}
}
const int MaxN = 16;
int sxy[1<<MaxN], zxy[1<<MaxN];
/** @brief Set c = sxy \times zxy */
void mul(int c[],int n){
FWT(sxy,n,1), FWT(zxy,n,1);
for(int i=0; i<(1<<n); ++i)
c[i] = 1ll*zxy[i]*sxy[i]%Mod;
FWT(c,n,-1); // get result
}
/** @brief a += b ( len = 1 << n ) */
void add(int a[],int b[],int n){
for(int i=0; i<(1<<n); ++i)
a[i] = (a[i]+b[i])%Mod;
}
int b[MaxN+1][1<<MaxN];
int g[1<<MaxN]; // calc (1 << cnt)
void prepare(int l,int r,int dep){
if(r-l == 1){
g[l] = qkpow(2,g[l]);
return void(b[dep][l] = g[l]-1);
}
int m = (l+r)>>1; // middle
prepare(l,m,dep-1);
prepare(m,r,dep-1);
/* sxy = LEFT, zxy = RIGHT */ ;
memcpy(sxy,b[dep-1]+l,(r-l)<<2);
memset(sxy+m-l,0,(r-m)<<2);
memcpy(zxy,b[dep-1]+l,(r-l)<<2);
memset(zxy,0,(m-l)<<2);
/* b = zxy * sxy + zxy + sxy */ ;
mul(b[dep]+l,dep);
add(b[dep]+l,b[dep-1]+l,dep);
}
int now[MaxN+1][1<<MaxN];
int fuck_xez; // very right point
int ans[1<<MaxN];
void solve(int l,int r,int dep){
if(r-l == 1){
if(g[l] == 1) // no such thing
return ; // forget it
ans[l] = 1ll*g[l]*inv2%Mod;
ans[l] = 1ll*ans[l]*now[dep][0]%Mod;
ans[l] = (ans[l]+Mod)%Mod;
return ;
}
int m = (l+r)>>1; // middle
/* Go to left part */ ;
memcpy(sxy,now[dep],(r-l)<<2);
memcpy(zxy,b[dep-1]+l,(r-l)<<2);
memset(zxy,0,(m-l)<<2);
mul(now[dep-1],dep); // now * b
add(now[dep-1],now[dep],dep); // + now
if(r == fuck_xez) // + b
add(now[dep-1]+m-l,b[dep-1]+m,dep-1);
memcpy(now[dep-1],now[dep-1]+m-l,(m-l)<<2);
solve(l,m,dep-1);
/* Go to right part */ ;
memcpy(sxy,now[dep],(r-l)<<2);
memcpy(zxy,b[dep-1]+l,(r-l)<<2);
memset(zxy+m-l,0,(r-m)<<2);
mul(now[dep-1],dep); // now * b
add(now[dep-1],now[dep],dep); // + now
if(r == fuck_xez) // + b
add(now[dep-1],b[dep-1]+l,dep-1);
add(now[dep-1],now[dep-1]+m-l,dep-1);
solve(m,r,dep-1);
}
vector< int > ys, cnt;
int add(int x){
int S = 0, len = ys.size();
for(int i=0; i<len; ++i){
int p = 0;
for(; x%ys[i]==0; ++p)
x /= ys[i];
if(p == 0) // gcd
S |= (1<<(i<<1));
if(p == cnt[i])
S |= (1<<(i<<1|1));
}
return S;
}
int main(){
int n = readint(); int N = n;
int G = readint(); int GG = G;
int L = readint(); int LL = L;
bool bad = (L%G != 0);
L /= G, n /= G; // only k*G
G = L; // save archive
/* Sieve factor of L */ ;
int len = 0; // ys.size()
for(int i=2; i<=L/i; ++i)
if(L%i == 0){
ys.push_back(i);
cnt.push_back(0);
for(; L%i==0; L/=i)
++ cnt[len];
++ len;
}
if(L != 1){
ys.push_back(L);
cnt.push_back(1);
++ len;
}
/* Calculate size of each type */ ;
for(int i=1; i<=G/i&&i<=n; ++i)
if(G%i == 0){ // G = lcm'
++ g[add(i)];
if(G/i <= n && i != G/i)
++ g[add(G/i)];
}
/* Prepare b in advance */ ;
fuck_xez = 1<<len<<len;
prepare(0,(1<<len<<len),len<<1);
/* Solve all answers */ ;
solve(0,(1<<len<<len),len<<1);
/* Tackle all queries */ ;
int q = readint();
for(int x; q; --q){
x = readint();
if(bad || x > N || LL%x || x%GG){
puts("0"); continue;
}
if(LL == GG){
printf("%d\n",x == GG);
continue;
}
printf("%d\n",ans[add(x/GG)]);
}
return 0;
}