Luogu P5366 [SNOI2017]遗失的答案
题目
筛出\(L\)的每个质因子,限制条件实际上就是限制了每个质因子的出现次数\([l,r]\),且\(l,r\)都有正好取到的。
显然\(L\)最多有\(8\)个不同的因子,所以对每个数而言,我们对其每个质因子是否取到\(l\)或\(r\)状压,这样\([1,n]\)的数就可以表示成一个\(16\)位的二进制数。
那么我们要求的就是在强制选某个二进制数的情况下,选出一些二进制数使得其按位或为全集的方案数。
令\(f(S)\)表示选出一些二进制数使得其按位或为\(S\)的方案数。
考虑容斥,设\(g(S)=\sum\limits_{T\subseteq S}f(T)\)。
那么显然的,记\(cnt_S\)表示有多少个二进制数是\(S\)的子集,\(g(S)=2^{cnt_S}\)。
那么我们容斥即可得到\(f(S)=\sum\limits_{T\subseteq S}(-1)^{|S|-|T|}g(T)\)。
然后考虑计算强制选择某个二进制数时的答案。
设这个数为\(T\),其补集为\(S\)。
那么我们需要枚举的就是\(S\)的超集(即子集包含\(S\)的集合)。
这个可以转化为枚举\(S\)的子集,然后枚举的超集就是\(T\cup S\)。
加上记忆化之后复杂度为\(3^{16}\)。
#include<bits/stdc++.h>
#define pi pair<int,int>
#define pb push_back
#define fi first
#define se second
#define count __builtin_popcount
using namespace std;
const int N=100007,P=1000000007;
namespace IO
{
char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[11],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
void Put(char x){*oS++=x;if(oS==oT)Flush();}
//int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
void write(int x){int top=0;if(!x)Put('0');while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put('\n');}
}
using namespace IO;
int read(){int x;cin>>x;return x;}
int inc(int a,int b){a+=b;return a>=P? a-P:a;}
int dec(int a,int b){a-=b;return a<0? a+P:a;}
int mul(int a,int b){return 1ll*a*b%P;}
int n,G,L,Q,l[N],r[N],p2[N],sz[N],U,len,ans[1<<17];vector<pi>fac;unordered_map<int,int>mp;
int GetS(int x)
{
int S=0,i,cnt;
for(i=0;i<len;++i)
{
for(cnt=0;!(x%fac[i].fi);++cnt) x/=fac[i].fi;
S|=(cnt==l[i])<<i|(cnt==r[i])<<i+len;
}
return S;
}
void initfac(int x=L)
{
for(int i=2,cnt=0;i*i<=x;++i)
if(!(x%i))
{
for(cnt=0;!(x%i);x/=i,++cnt);
fac.pb(pi(i,cnt));
}
if(x^1) fac.pb(pi(x,1));
}
void initpow()
{
p2[0]=1,len=fac.size(),U=(1<<len*2)-1;
for(int i=1;i<=U;++i) p2[i]=inc(p2[i-1],p2[i-1]);
}
void initlr()
{
for(int i=0,x;i<len;++i)
{
r[i]=fac[i].se;
for(x=G;!(x%fac[i].fi);++l[i]) x/=fac[i].fi;
}
}
void initagg()
{
for(int i=1;i*i<=L;++i)
if(!(L%i))
{
if(!(i%G)&&i<=n) mp[i]=GetS(i);
if(i*i^L&&!(L/i%G)&&L/i<=n) mp[L/i]=GetS(L/i);
}
len<<=1;
for(auto x:mp) ++sz[x.se];
for(int i=0,S;i<len;++i) for(S=0;S<=U;++S) if(S&1<<i) sz[S]+=sz[S^1<<i];
}
int solve(int x)
{
int T=mp[x],S=U^T,sum=count(T)&1? (P-p2[sz[T]-1]):(p2[sz[T]-1]);
if(ans[T]) return ans[T];
for(int s=S;s;s=s-1&S)
if(count(s|T)&1) sum=dec(sum,p2[sz[s|T]-1]);
else sum=inc(sum,p2[sz[s|T]-1]);
return ans[T]=sum;
}
int main()
{
n=read(),G=read(),L=read(),Q=read();
if(L%G) { while(Q--) puts("0"); return 0; }
initfac(),initpow(),initlr(),initagg();
for(int x;Q;--Q) x=read(),write(x%G||L%x||x>n? 0:solve(x));
return Flush(),0;
}