题目链接
小皮球在计算出答案之后,买了一堆皮肤,他心里很开心,但是一不小心,就忘记自己买了哪些皮肤了。==|||万
幸的是,他还记得他把所有皮肤按照1~N来编号,他买来的那些皮肤的编号(他至少买了一款皮肤),最大公约数
是G,最小公倍数是L。现在,有Q组询问,每组询问输入一个数字X,请你告诉小皮球,有多少种合法的购买方案中
,购买了皮肤X?因为答案太大了,所以你只需要输出答案mod1000000007即可。
一道很好的计数题
我们先来考虑没有必选的限制怎么做
首先 L L L 肯定是 G G G 的倍数 ,否则无解 , 那么我们可以考虑把问题转化
令 L / = G , N / = G L/=G,N/=G L/=G,N/=G 这样问题就变成了 从 1 ∼ N 1 \sim N 1∼N 选出一些最大公约数是 1 的数 , 使得他们的 L C M LCM LCM 是 L L L
(似乎并没有简单些什么…)
我们考虑怎么来达成这种条件 , 首先注意到选择的数一定会是 L L L 的约数 , 不然 L C M LCM LCM就不会是 L L L , 这样我们选数的范围就变少了 , 因为约数个数不会非常多
但是爆搜还是肯定过不去的
我们需要再次考虑一下 G C D 和 L C M GCD 和 LCM GCD和LCM的本质
假设我们把 L L L 质因数分解, G G G在这里就已经看作是 1 了可以不管:
L = ∏ i = 1 k p i q i L=\prod_{i=1}^{k} p_i^{q_i} L=i=1∏kpiqi
那么考虑选出来的数们:
a j = ∏ i = 1 k p i q i j a_j=\prod_{i=1}^{k} p_i^{q_{i_j}} aj=i=1∏kpiqij
我们可以发现这样一个很直观的东西:
G C D = ∏ i = 1 k m i n { p i q i j } GCD=\prod_{i=1}^k min\{ p_i^{q_{i_j}}\} GCD=i=1∏kmin{piqij}
L C M = ∏ i = 1 k m a x { p i q i j } LCM=\prod_{i=1}^k max\{ p_i^{q_{i_j}}\} LCM=i=1∏kmax{piqij}
到这里就非常好写了 , 因为这样的意思就是说 , 对于任意一个 L L L的质因子必须满足,在选出来的数中 , 至少有一个数没有包含该质因子 , 至少要有一个数包含了该质因子的最高次项
而108 以内的数包含的质因数个数最多 只有 8 个 , 所以直接状压一个16长度的二进制状态表示这两个条件对于某质因子是否满足就可以 dp 了呢
但是有必选限制怎么办呢?
直接前后各做一遍 dp 然后拼起来最后把当前必选数加入就行了 , 但是合并似乎要用到 FWT 的或卷积 暴力卷起来就复杂度不对了 , 本人太菜了根本不会FWT , 所以只能放弃这个做法…
我们发现这个东西就是相当于在 d ( L ) d(L) d(L) 个数中选出一些数表示的状态或起来使得状态是全集的方案数 , 发现二进制位数很少 , 我们想到了容斥
那么可以直接设, F ( S ) F(S) F(S) 表示选出一些数或成的数是 S 的子集的方案数 , 这样我们就可以容斥求出没有限制情况下的方案数了
那么怎么求 F ( S ) F(S) F(S) 呢 , 这个太简单了 , 因为要或出 S 的子集,首先得保证你自己就是 S 的子集 , 而且满足这个条件之后随便或都是满足的 , 所以 F ( S ) F(S) F(S) 就是 2 的本来就是 S 子集的数的个数 的次方 ,即 F ( S ) = 2 c n t T ,      T ∈ S F(S)=2^{cnt_T} ,\;\; T\in S F(S)=2cntT,T∈S
那么接下来考虑 必选限制
我们发现必选之后 , 我们接下来选的数只需要满足能把它这个状态中的 0 给全部补成 1 就可以了,比方说这个数表示的状态是 11000011 11000011 11000011 ,那么我们的目标要或出的集合就是 00111100 00111100 00111100的超集
怎么求这个呢?
现在我们 的目标是 00111100 00111100 00111100 的超集 , 由于如果我们或出全集一定是合法的 , 所以我们应该还是要枚举全集的一些子集 , 但肯定不是都要计算
那么要算哪一些呢
由于我们只关心要或出来的缺少的1 , 其他的不用管
那么方案应该是 把剩下的1全部或出来的方案 - 至少一个没有或出来的方案 + 至少两个没有或出来的方案 - …
而我们的 F ( S ) F(S) F(S) 表示的是或出 S S S 的子集的方案 , 所以除开要考虑的位 , 其他的位都是 1 就可以了 , 就用这些 F F F 来进行容斥 , 容斥系数直接和上面一样算也可以 , 手推一下就可以发现是一样的
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read()
{
int x=0;char ch=getchar();int t=1;
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
return x*t;
}
const int N=805,mod=1e9+7,MAXN=1<<16;
int n,G,L,Q;
int si[N],num[N],cnt=0,cur=0;
int UP;
map<int,int> Ans;
inline int getsta(int X){
int state=0;
for(int i=1;i<=cnt;++i) {
if(X%si[i]==0) {
int tot=1;X/=si[i];
while(X%si[i]==0) ++tot,X/=si[i];
if(tot==num[i]) state|=1<<i-1;
}
else state|=1<<i+cnt-1;
}
return state;
}
struct frac {
int data,state;
inline bool operator <(frac b)const{return data<b.data;}
inline void init(int X) {data=X;state=getsta(X);return;}
}T[N];
int gcd(int a,int b){return b? gcd(b,a%b):a;}
int ans[N],Cnt[MAXN],bin[MAXN],high;
inline void upd(int &x,int y){x+=y;if(x>=mod) x-=mod;return;}
inline void dec(int &x,int y){x-=y;if(x<0) x+=mod;}
#define lowbit(a) ((a)&(-a))
inline int Bitcount(int x){int ret=0;for(;x;x-=lowbit(x),++ret);return ret;}
inline void Bitout(int x){
for(int i=high-1;~i;--i) putchar(((x>>i)&1)+'0');putchar(' ');
}
inline int solve(){
cnt=0;int ed=sqrt(L),R=L;
for(int i=2;i<=sqrt(L);++i) {
if(L%i==0) {
si[++cnt]=i;num[cnt]=1;L/=i ;
while(L%i==0) ++num[cnt],L/=i;
}
}
bin[0]=1;
for(int i=1;i<MAXN;++i) bin[i]=bin[i-1],upd(bin[i],bin[i]);
if(L!=1) si[++cnt]=L,num[cnt]=1;
high=cnt<<1;UP=1<<high;
for(int i=1;i<=ed&&i<=n;++i) if(R%i==0) {T[++cur].init(i);if(R/i!=i&&R/i<=n) T[++cur].init(R/i);}
sort(T+1,T+1+cur);for(int i=1;i<=cur;++i) ++Cnt[T[i].state];
for(int i=0;i<high;++i) for(int s=0;s<UP;++s) if(s&(1<<i)) {Cnt[s]+=Cnt[s^(1<<i)];}
int ans=0;for(int s=0;s<UP;++s) {(Bitcount(s)&1)? dec(ans,bin[Cnt[s]]):upd(ans,bin[Cnt[s]]);}
return ans;
}
inline int solve(int x)
{
if(Ans.count(x)) return Ans[x];
int pos=lower_bound(T+1,T+1+cur,(frac){x,0})-T;
if(T[pos].data!=x) return 0;
register int state=T[pos].state;register int inv=(UP-1)^state;
int ans=0;
for(register int s=inv;~s;s=(s-1)&inv) {register int T=s|state; (Bitcount(T)&1)? dec(ans,bin[Cnt[T]-1]):upd(ans,bin[Cnt[T]-1]); if(!s) break;}
return Ans[x]=ans;
}
int main()
{
n=read();G=read();L=read();Q=read();
bool fl=0;int R=L;
if(L%G!=0) fl=1;L/=G;n/=G;
solve();
int a;
for(int i=1;i<=Q;++i) {
a=read();
if(fl||(a%G!=0)||(R%(a/G)!=0)) puts("0");
else printf("%d\n",solve(a/G));
}
return 0;
}