题目大意
有 n n n个皮肤,编号为 1 1 1到 n n n。小皮球买了若干个皮肤,这些皮肤的编号的最大公约数为 G G G,最小公倍数为 L L L。
但是,他忘了自己买了哪些皮肤。现在,有 q q q组询问,每组询问有一个数字 x x x,求有多少种合法的方案中,购买了皮肤 x x x?
对于每组询问,输出答案模 1 0 9 + 7 10^9+7 109+7后的值。
1 ≤ n , G , L ≤ 1 0 8 1\leq n,G,L\leq 10^8 1≤n,G,L≤108, 1 ≤ q ≤ 1 0 5 1\leq q\leq 10^5 1≤q≤105, 1 ≤ x ≤ 1 0 8 1\leq x\leq 10^8 1≤x≤108
题解
转化题意
题意可转化为:求在 1 1 1到 ⌊ n G ⌋ \lfloor\dfrac{n}{G}\rfloor ⌊Gn⌋中选择最大公约数为 1 1 1、最小公倍数为 L G \dfrac{L}{G} GL的合法方案中,购买的皮肤 x G \dfrac{x}{G} Gx的方案的数量。
当然,如果 L ∤ G L\nmid G L∤G或者 x ∤ G x\nmid G x∤G,则方案数为 0 0 0。
下面, L = L G , x = x G L=\dfrac{L}{G},x=\dfrac{x}{G} L=GL,x=Gx
状压 D P DP DP
因为 L ≤ 1 0 8 L\leq 10^8 L≤108,分解质因子后,质因子的数量不超过 8 8 8个。考虑状压 D P DP DP。
若要保证最大公约数为 1 1 1,则对于每一个 L L L的质因子,都应存在至少一个皮肤,使得这个皮肤的编号分解质因数后这个质因子的指数为 0 0 0。
若要保证最小公倍数为 L L L,则对于每一个 L L L的质因子,都应存在至少一个皮肤,使得这个皮肤的编号分解质因数后这个质因子的指数与 L L L的相同。
设 L L L的质因子个数为 k k k,则状压的长度为 2 k 2k 2k。前 k k k个位置表示是否有数的这个质因子的指数为 0 0 0,后 k k k个位置表示是否有数的这个质因子的指数达到上界。这个状态表示的对象是皮肤的编号,而需要用到的皮肤编号只有 L L L的因子。
看似最多有 2 2 k 2^{2k} 22k种状态,但如果质因子的指数为 1 1 1,则一个数要么为 0 0 0,要么达到上界。而如果不是 1 1 1,那 k k k就不再能取到 8 8 8。指数不是 1 1 1的质因子个数越多, k k k就越小。经过计算,最终的总状态数在 650 650 650之内。
设 f i , j f_{i,j} fi,j表示前 i i i个数选若干个数后状态为 j j j的方案数, g i , j g_{i,j} gi,j表示第 i i i个数及之后的数中选若干个数后状态为 j j j的方案数。
设第 i i i个数的状态为 s t st st,则 x x x为第 i i i个数时的答案为
a n s [ i ] = ∑ S ∣ s t = 2 2 k − 1 ∑ x ∣ y = S f i − 1 , x × g i + 1 , y ans[i]=\sum\limits_{S|st=2^{2k}-1}\sum\limits_{x|y=S}f_{i-1,x}\times g_{i+1,y} ans[i]=S∣st=22k−1∑x∣y=S∑fi−1,x×gi+1,y
F W T FWT FWT
那怎么求上面这个式子呢?用 F W T FWT FWT即可。
那么,因为 x x x一定是 G G G的因数,所以在 a n s ans ans数组中一定能找到对应的答案。那么查询就是 O ( 1 ) O(1) O(1)的了。
时间复杂度为 O ( L + v × 2 2 k × 2 k + q log v ) O(\sqrt L+v\times 2^{2k}\times 2k+q\log v) O(L+v×22k×2k+qlogv)。其中 v v v表示状态数, k k k表示 L L L的质因子个数。 v v v的最大值为 650 650 650, k k k的最大值为 8 8 8。因为 v v v和 k k k不能同时取到最大值,而且跑不满,时限有 2 s 2s 2s,所以还是可以过的。
code
#include<bits/stdc++.h>
using namespace std;
int n,G,L,qt,all,cnt=0,p[15],q[15],v[655],num[100005],s[1<<16];
int w[655],ans[655],f[655][1<<16],g[655][1<<16],h[655][1<<16];
const int mod=1000000007;
void pt(int x){
for(int i=2;i*i<=x;i++){
if(x%i==0){
p[++p[0]]=i;
while(x%i==0){
x/=i;++q[p[0]];
}
}
}
if(x>1){
p[++p[0]]=x;q[p[0]]=1;
}
}
int gt(int x){
int re=0;
for(int i=1;i<=p[0];i++){
int vt=0;
while(x%p[i]==0){
x/=p[i];++vt;
}
if(vt==0) re|=(1<<i-1);
if(vt==q[i]) re|=(1<<i-1+p[0]);
}
return re;
}
int mi(int t,int v){
if(!v) return 1;
int re=mi(t,v/2);
re=1ll*re*re%mod;
if(v&1) re=1ll*re*t%mod;
return re;
}
void fwt_or(int *e,int fl){
for(int s=2;s<=all;s<<=1){
int mid=s>>1;
for(int bl=0;bl<all;bl+=s){
for(int i=0;i<mid;i++){
e[bl+mid+i]=(e[bl+mid+i]+1ll*fl*e[bl+i]+mod)%mod;
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&G,&L);
if(L%G){
int x;
scanf("%d",&qt);
while(qt--){
scanf("%d",&x);
printf("0\n");
}
return 0;
}
L/=G;n/=G;pt(L);
for(int i=1;i*i<=L&&i<=n;i++){
if(L%i==0){
num[++num[0]]=i;
if(L/i<=n&&L/i!=i) num[++num[0]]=L/i;
}
}
for(int i=1;i<=num[0];i++){
++s[gt(num[i])];
}
all=1<<(p[0]*2);
for(int i=0;i<all;i++){
if(s[i]){
v[++cnt]=i;w[cnt]=mi(2,s[i])-1;
}
}
f[0][0]=g[cnt+1][0]=1;
for(int i=1;i<=cnt;i++){
for(int j=0;j<all;j++){
f[i][j]=(f[i][j]+f[i-1][j])%mod;
f[i][j|v[i]]=(f[i][j|v[i]]+1ll*f[i-1][j]*w[i]%mod)%mod;
}
}
for(int i=cnt;i;i--){
for(int j=0;j<all;j++){
g[i][j]=(g[i][j]+g[i+1][j])%mod;
g[i][j|v[i]]=(g[i][j|v[i]]+1ll*g[i+1][j]*w[i]%mod)%mod;
}
}
for(int i=0;i<=cnt;i++){
fwt_or(f[i],1);fwt_or(g[i+1],1);
}
for(int i=1;i<=cnt;i++){
for(int j=0;j<all;j++){
h[i][j]=1ll*f[i-1][j]*g[i+1][j]%mod;
}
fwt_or(h[i],-1);
}
for(int i=1;i<=cnt;i++){
for(int j=0;j<all;j++){
if((j|v[i])==all-1) ans[i]=(ans[i]+h[i][j])%mod;
}
ans[i]=1ll*ans[i]*mi(2,s[v[i]]-1)%mod;
}
int x;
scanf("%d",&qt);
while(qt--){
scanf("%d",&x);
if(x%G){
printf("0\n");
continue;
}
x/=G;
if(L%x||x>n){
printf("0\n");
continue;
}
int vt=lower_bound(v+1,v+cnt+1,gt(x))-v;
printf("%d\n",ans[vt]);
}
return 0;
}