遗失的答案
题解
其实这题挺水的。
显然,当
G
∤
L
G\not | \,\,\,L
G∣L时是无解的。我们可以先将小于
n
n
n的不是
G
G
G倍数的值全部舍去,只用管为
G
G
G倍数的数。
当出去
G
G
G后,剩下的数的最大公约数一定是
1
1
1,最小公倍数一定是
L
G
\frac{L}{G}
GL。我们先将
L
G
\frac{L}{G}
GL进行质因数分解,记
L
G
=
∏
p
i
a
i
\frac{L}{G}=\prod p_{i}^{a_{i}}
GL=∏piai。
容易发现,对于每个
p
i
p_{i}
pi,一定有数不是
p
i
p_{i}
pi的倍数,一定有数是
p
i
a
i
p_{i}^{a_{i}}
piai的倍数,且所有数含有的
p
i
p_{i}
pi个数一定在
[
0
,
a
i
]
[0,a_{i}]
[0,ai]之间。
又因为,
L
≤
1
0
8
L\leq 10^8
L≤108,故不同
p
i
p_{i}
pi的个数不会超过
8
8
8。于是我们就想到了状压。
我们需要同时记录下两个状态
(
S
1
,
S
2
)
(S1,S2)
(S1,S2),其中
S
1
S1
S1表示不含
p
i
p_{i}
pi,
S
2
S2
S2表示含
p
i
a
i
p_{i}^{a_{i}}
piai,我们将
S
1
S
2
S1S2
S1S2拼在一起,记作
S
S
S。
容易发现,每个是
G
G
G倍数也是
L
L
L因数的数都可以用一个状态
S
S
S表示,而因为如果它的含有
p
i
p_{i}
pi个数在
(
0
,
a
i
)
(0,a_{i})
(0,ai)中的话不会影响状态,所以我们可以先将这样的数全部统计一下,用
t
S
t_{S}
tS表示达到状态为
S
S
S的不选状态
S
S
S以外的数的选数方法的个数。
容易发现不同的
S
S
S个数是不会超过
600
600
600的(这里指的因数只考虑
p
i
p_{i}
pi的次数满的与空的两种情形),可以通过暴力进行证明,我们将这样的
S
S
S记录下来。
我们可以先设出两个
d
p
dp
dp,
f
i
,
S
f_{i,S}
fi,S表示选完前
i
i
i个状态,现在的总状态为
S
S
S时的方案数。
同样的,
g
i
,
S
g_{i,S}
gi,S表示选完后
i
i
i个状态,现在的总状态为
S
S
S时的方案数。
至于如何处理必须选一个数的方案数,我们可以先将这个数的状态求出来,求出在这个状态满足其的数少了一个的情况下的
d
p
dp
dp即可。
由于我们已经处理过前缀与后缀,我们可以将其两边的
d
p
dp
dp合起来,再加上少了一个的方案数。
由于合起来有涉及到状态的按位或,有
a
n
s
i
,
j
=
∑
x
∣
y
=
j
f
i
−
1
,
x
g
i
+
1
,
y
ans_{i,j}=\sum_{x|y=j}f_{i-1,x}g_{i+1,y}
ansi,j=∑x∣y=jfi−1,xgi+1,y。
容易发现,这就是一个可以用
F
W
T
FWT
FWT解决的板子式子,可以通过
F
W
T
FWT
FWT将这个问题简单的解决。
记 m m m为不同的状态数,时间复杂度为 O ( n m l o g n ) O\left(nmlog\,n\right) O(nmlogn)。
题解
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 70005
#define MAXM 655
#define reg register
typedef long long LL;
const int mo=1e9+7;
template<typename _T>
inline void read(_T &x){
_T f=1;x=0;char s=getchar();
while('0'>s||'9'<s){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
inline int add(const int x,const int y){return x+y<mo?x+y:x+y-mo;}
inline void Add(int &x,const int y){x=add(x,y);}
inline int qkpow(int a,int s){int t=1;while(s){if(s&1)t=1ll*a*t%mo;a=1ll*a*a%mo;s>>=1;}return t;}
int n,G,L,q,prime[20],cnt[20],cntp,idx,res[MAXN],ans[MAXM][MAXN];
int s[MAXN],num[MAXN],t[MAXN],f[MAXM][MAXN],g[MAXM][MAXN];
inline void devide(int x){
for(reg int i=2;i*i<=x;++i)
if(x%i==0){
prime[++cntp]=i;cnt[cntp]=0;
while(x%i==0)x/=i,++cnt[cntp];
}
if(x>1)prime[++cntp]=x,cnt[cntp]=1;
}
void dfs(const int dep,int cur,const int s1,const int s2){
if(dep>cntp)return (void)(++s[s1|(s2<<cntp)]);
for(reg int i=0;i<=cnt[dep];++i){
dfs(dep+1,cur,s1|((i==0)<<dep-1),s2|((i==cnt[dep])<<dep-1));
cur*=prime[dep];if(cur>n)return ;
}
}
inline void FWT(int *A,const int lim,const int typ){
for(reg int k=1;k<lim;k<<=1)
for(reg int i=0;i<lim;i+=(k<<1))
for(reg int j=i;j<i+k;++j)
Add(A[j+k],typ?A[j]:mo-A[j]);
}
inline int calc(int x){
int s1=0,s2=0;
for(reg int i=1;i<=cntp;++i){
int s=0;while(x%prime[i]==0)x/=prime[i],s++;
s1|=(s==0)<<i-1,s2|=(s==cnt[i])<<i-1;
}
return s1|(s2<<cntp);
}
signed main(){
read(n);read(G);read(L);read(q);
if(L%G){while(q--)puts("0");return 0;}
n/=G;L/=G;devide(L);dfs(1,1,0,0);const int lim=(1<<cntp+cntp);
for(reg int i=0;i<lim;++i)if(s[i])num[++idx]=i,t[idx]=qkpow(2,s[i])-1;
f[0][0]=g[idx+1][0]=1;
for(reg int i=0;i<idx;++i)
for(reg int j=0;j<lim;++j)
Add(f[i+1][j],f[i][j]),Add(f[i+1][j|num[i+1]],1ll*f[i][j]*t[i+1]%mo);
for(reg int i=idx+1;i>1;--i)
for(reg int j=0;j<lim;++j)
Add(g[i-1][j],g[i][j]),Add(g[i-1][j|num[i-1]],1ll*g[i][j]*t[i-1]%mo);
for(reg int i=0;i<=idx+1;++i)FWT(f[i],lim,1),FWT(g[i],lim,1);
for(reg int i=1;i<=idx;++i)
for(reg int j=0;j<lim;++j)
Add(ans[i][j],1ll*f[i-1][j]*g[i+1][j]%mo);
for(reg int i=1;i<=idx;++i)FWT(ans[i],lim,0);
for(reg int i=1;i<=idx;++i){
for(reg int j=0;j<lim;++j)
if((j|num[i])==lim-1)Add(res[i],ans[i][j]);
res[i]=1ll*res[i]*qkpow(2,s[num[i]]-1)%mo;
}
for(reg int i=1;i<=q;++i){
int x;read(x);if(x%G){puts("0");continue;}
x/=G;if(L%x||x>n){puts("0");continue;}
const int y=calc(x),p=lower_bound(num+1,num+idx+1,y)-num;
printf("%d\n",res[p]);
}
return 0;
}