「SNOI2017」遗失的答案
「SNOI2017」遗失的答案
传送门
题解
首先可以考虑把\(n \rightarrow n/G,L \rightarrow L/G\),这是很显然的.
然后考虑所有可能出现的质因数就是\(L\)的质因数,所以可以对\(L\)进行质因数分解.
接着考虑设\(f_{S1,S2}\)表示最小集为\(S1\),最大集为\(S2\)的状态的方案数,这个很显然可以\(dp\).
这个时候我们需要强制一个数字在里面,相当于是强制他不在里面然后查他的超集的方案数.
所以可以前后缀\(dp\)然后做一个\(FMT\),求出来每一个不在里面的方案数,最后做一个超集和就可以了.
代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define ll long long
#define REP(a,b,c) for(int a=b;a<=c;a++)
#define re register
#define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
typedef pair<int,int> pii;
#define mp make_pair
inline int gi()
{
int f=1,sum=0;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return f*sum;
}
const int N=10010,LIM=1<<16,MX=18,Mod=1e9+7,INV=500000004;
int n,G,L,Q,prime[N],s,p[N],mx[N],tot,cnt[LIM];
bool is_prime[N];
void init()
{
is_prime[1]=1;
for(int i=2;i<=10000;i++)
{
if(!is_prime[i])prime[++s]=i;
for(int j=1;j<=s&&prime[j]*i<=10000;j++)
{
is_prime[prime[j]*i]=1;
if(!(i%prime[j]))break;
}
}
}
void calc(int x)
{
for(int i=1;i<=s;i++)
if(x%prime[i]==0)
{
p[++tot]=prime[i];
while(x%prime[i]==0)x/=prime[i],mx[tot]++;
}
if(x>1)p[++tot]=x,mx[tot]=1;
}
void dfs(int d,int s,int S1,int S2)
{
if(d>tot){cnt[S1|(S2<<tot)]++;return;}
for(int i=0;i<=mx[d];i++)
{
dfs(d+1,s,S1|(i==0?1<<(d-1):0),S2|(i==mx[d]?1<<(d-1):0));
if(s*p[d]>n)break;
s*=p[d];
}
}
int o[LIM],S[LIM],suf[810][LIM],pre[810][LIM],f[LIM],tmp[LIM];
int qpow(int a,int b){int ret=1;while(b){if(b&1)ret=1ll*ret*a%Mod;b>>=1;a=1ll*a*a%Mod;}return ret;}
void FMT(int *a,int limit,int opt)
{
for(int i=1;i<limit;i<<=1)
for(int R=i<<1,j=0;j<limit;j+=R)
for(int k=0;k<i;k++)
if(opt==1)a[i+j+k]=(a[i+j+k]+a[j+k])%Mod;
else a[i+j+k]=(a[i+j+k]-a[j+k]+Mod)%Mod;
}
void FAT(int *a,int limit,int opt)
{
for(int i=1;i<limit;i<<=1)
for(int R=i<<1,j=0;j<limit;j+=R)
for(int k=0;k<i;k++)
if(opt==1)a[j+k]=(a[j+k]+a[i+j+k])%Mod;
else a[j+k]=(a[j+k]-a[i+j+k]+Mod)%Mod;
}
int get(int x)
{
int S1=0,S2=0;
for(int i=1;i<=tot;i++)
{
int cnt=0;
while(x%p[i]==0)x/=p[i],cnt++;
if(cnt==0)S1|=1<<(i-1);
if(cnt==mx[i])S2|=1<<(i-1);
}
return S1|(S2<<tot);
}
int main()
{
n=gi();G=gi();L=gi();Q=gi();init();
if(L%G){while(Q--)puts("0");return 0;}
L/=G;n/=G;calc(L);dfs(1,1,0,0);int lim=1<<(tot<<1);s=0;
for(int i=0;i<lim;i++)
if(cnt[i])o[++s]=i,S[s]=qpow(2,cnt[i])-1;
for(int i=1;i<=s;i++)cnt[i]=S[i];
f[0]=1;pre[0][0]=1;
for(int i=1;i<=s;i++)
{
int x=o[i];
for(int j=0;j<lim;j++)
tmp[j|x]=(tmp[j|x]+1ll*f[j]*cnt[i]%Mod)%Mod;
for(int j=0;j<lim;j++)f[j]=(f[j]+tmp[j])%Mod,tmp[j]=0;
for(int j=0;j<lim;j++)pre[i][j]=f[j];
}
memset(f,0,sizeof(f));
f[0]=1;suf[s+1][0]=1;
for(int i=s;i;i--)
{
int x=o[i];
for(int j=0;j<lim;j++)
tmp[j|x]=(tmp[j|x]+1ll*f[j]*cnt[i]%Mod)%Mod;
for(int j=0;j<lim;j++)f[j]=(f[j]+tmp[j])%Mod,tmp[j]=0;
for(int j=0;j<lim;j++)suf[i][j]=f[j];
}
for(int i=0;i<=s;i++)FMT(pre[i],lim,1);
for(int i=1;i<=s+1;i++)FMT(suf[i],lim,1);
for(int i=0;i<=s;i++)
for(int j=0;j<lim;j++)
pre[i][j]=1ll*pre[i][j]*suf[i+2][j]%Mod;
for(int i=0;i<=s;i++)FMT(pre[i],lim,-1),FAT(pre[i],lim,1);
while(Q--)
{
int x=gi();
if(x%G){puts("0");continue;}
x/=G;
if(L%x||x>n){puts("0");continue;}
int ss=get(x),p=lower_bound(o+1,o+s+1,ss)-o-1;
int v=pre[p][(lim-1)^ss];
printf("%lld\n",1ll*v*(cnt[p+1]+1)%Mod*INV%Mod);
}
return 0;
}