P5366-[SNOI2017]遗失的答案【状压dp,FWT】
正题
题目链接:https://www.luogu.com.cn/problem/P5366
题目大意
给出一个\(n,G,L\)。
\(q\)次询问在\(1\sim n\)中选择若干个数字并且数字\(x\)必选,要求这些数的\(gcd\)为\(G\)且\(lcm\)为\(L\)的方案数。
\(1\leq n,G,L,x\leq 10^8,1\leq q\leq 10^5\)
解题思路
我们令\(m=\frac{L}{G},x=\frac{x}{G},n=\lfloor\frac{n}{G}\rfloor\),那么就是求选\(1\sim m\)中的数的情况下\(gcd=1,lcm=m\)的方案。
发现对于每个\(m\)的分解后的质因数\(p^c\),我们选择的数的为\(p^k\),那么我们至少需要一个\(k=0\)和一个\(k=c\)。
也就是其实\(0<k<m\)的都是不会对这个质因数产生影响的,所以我们可以把一个质因子\(p\)分出两个状态,分别是\(k=0\)的和\(k=m\)的有没有。
同样的,我们用上面的状态\(S\)表示\(1\sim n\)的数字,会发现\(S\)最多只有六百出头种不同的取值。
那么我们把这些取值拿出来,把数字分成不同的类,这样我们就只需要考虑每个类的数字个数了。记\(f_{i,S}\)表示做完了前\(i\)类时状态为\(S\)的方案,同理\(g_{i,S}\)则表示做完了后\(i\)类。
这样我们可以用\(FWT\)快速处理出\(h_{i,S}\)表示处理了除了第\(i\)类以外的所有类,状态为\(S\)时的方案。
然后再考虑这一类有一个数字必选来转移每个\(h_{i,S}\)就知道每一类的答案了。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cctype>
using namespace std;
const int N=1<<16,M=610,P=1e9+7;
int n,G,L,q,m,tot,cnt,MS,num[N];
int p[99],c[99],pos[N],pw[M],rev[M];
int f[M][N],g[M][N],h[M][N];
int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return x*f;
}
int power(int x,int b){
int ans=1;
while(b){
if(b&1)ans=1ll*ans*x%P;
x=1ll*x*x%P;b>>=1;
}
return ans;
}
void FWT(int *f,int n,int op){
for(int p=2;p<=n;p<<=1)
for(int k=0,len=p>>1;k<n;k+=p)
for(int i=k;i<k+len;i++)
(f[i+len]+=f[i]*op)%=P;
return;
}
void dfs(int dep,int x,int s){
if(x>n)return;
if(dep>cnt){num[s]++;return;}
dfs(dep+1,x,s|(1<<dep+cnt-1));
for(int i=1,pw=1;i<=c[dep];i++)
pw=pw*p[dep],dfs(dep+1,x*pw,s|((i==c[dep])<<dep-1));
return;
}
void init(){
int x=m;
for(int i=2;i<=x;i++)
if(x%i==0){
p[++cnt]=i;
while(x%i==0)c[cnt]++,x/=i;
}
dfs(1,1,0);MS=(1<<cnt*2);
for(int i=0;i<MS;i++)
if(num[i]){
pos[i]=++tot;rev[tot]=i;
pw[tot]=power(2,num[i])-1;
}
f[0][0]=g[tot+1][0]=1;
for(int i=1;i<=tot;i++)
for(int j=0;j<MS;j++){
(f[i][j]+=f[i-1][j])%=P;
(f[i][j|rev[i]]+=1ll*f[i-1][j]*pw[i]%P)%=P;
}
for(int i=tot;i>=1;i--)
for(int j=0;j<MS;j++){
(g[i][j]+=g[i+1][j])%=P;
(g[i][j|rev[i]]+=1ll*g[i+1][j]*pw[i]%P)%=P;
}
for(int i=0;i<=tot;i++)FWT(f[i],MS,1);
for(int i=1;i<=tot+1;i++)FWT(g[i],MS,1);
for(int i=1;i<=tot;i++)
for(int j=0;j<MS;j++)
h[i][j]=1ll*f[i-1][j]*g[i+1][j]%P;
for(int i=1;i<=tot;i++){
FWT(h[i],MS,-1);
int k=power(2,num[rev[i]]-1);
for(int j=MS-1;j>=0;j--){
int r=h[i][j];h[i][j]=0;
(h[i][j|rev[i]]+=1ll*r*k%P)%=P;
}
}
return;
}
signed main()
{
n=read();G=read();L=read();q=read();
n/=G;m=L/G;init();
while(q--){
int x=read();
if(x%G){puts("0");continue;}
if(L%x){puts("0");continue;}
x/=G;
if(x>n){puts("0");continue;}
int S=((1<<cnt)-1)*(1<<cnt);
for(int i=1;i<=cnt;i++)
if(x%p[i]==0){
int k=0;S^=(1<<i+cnt-1);
while(x%p[i]==0)x/=p[i],k++;
if(k==c[i])S|=(1<<i-1);
}
cout<<(h[pos[S]][MS-1]+P)%P<<'\n';
}
return 0;
}