[SNOI2017]遗失的答案
题目
题解
第一个处理,我们可以将 \(N,L\) 同时 \(/G\),当然,如果 \(G\nmid L\),那么全部无解,输出 \(Q\) 个 0
即可。
令 \(n=\frac{N}{G},l=\frac{L}{G}\),那么,这道题就被我们转化为
在 \([1,n]\) 之间,选一些数,其中必选 \(\frac{X}{G}\),使得他们的 \(\gcd\) 为 \(1\),\(\text{lcm}\) 为 \(n\)。
注意,这里的 \(\gcd\) 为 \(1\) 不等于所有数互质。
显然,我们选出的这些数一定都是由 \(\text{lcm}\) 的因数,考虑我们选择 \(\{x_1,x_2,x_3...x_k\}\) 这些数,怎么才能使得他们的 \(\gcd=1,\text{lcm}=l\)。
由于 \(\gcd\) 与 \(\text{lcm}\) 十分类似,我们只分析 \(\gcd\):
将所有 \(x\) 分解,设 \(x_i=p_{i,j}^{t_{i,j}}p_{i,j+1}^{t_{i,j+1}}...\)
如果 \(\exist x_i,t_{i,j}=0\) 对于所有 \(j\) 成立,那么他们的 \(\gcd=1\) ,也就是说,只要我们选的所有数中,有某一个数的这个质因数指数为 \(0\),而且所有的质因数都可以找到类似于这样的一个数,那么 \(\gcd(x_i)=1\)。
类似地,\(\text{lcm}\) 也是同样的道理,只要我们选的所有数中,有某一个数的这个质因数指数和 \(l\) 相同,并且对于所有的质因数都可以找到这样的一个数,那么 \(\text{lcm}(x_i)=l\)。
有了这个分析,我们进一步思考。
由于 \(L\le 10^8\),我们可以看一下,\(L\) 最多会有多少个因数?
那么,\(L\) 最多只会有 \(9-1=8\) 个因数。
对于一个因数,我们可以维护他的每个质因数,因为只有八个因数,那么就会有 \(2^8\) ?并不是,由于我们每个因数都有 底下界( \(\gcd\) ),底上界( \(\text{lcm}\) ) 这两种情况,那么每个质因数我们维护两位二进制——\(00\) 表示上下界都不到达,\(01\) 表示底上界,\(10\) 表示底下界。
对于一个数 \(M\le 10^8\),我们有
它的因数个数最多不超过 \(800\) 个。
的结论,具体证明在这里。
那么,我们来看一下怎么枚举因数的:
void dfs(const int i,int now,const int s){
if(i==len){//枚举了一个因数
ans[++cnt]=-1;//更新因数编号
lst.push_back(mp(now,cnt));//并将其放进所有的因数集合中
// printf("this num,now == %d, cnt == %d, s == ",now,cnt);print(s);Endl;
f[cnt].resize(sz);
// printf("modify_DWT(%d,%d,%d)\n",cnt,0,1);
// printf("modify_DWT(%d,%d,%d)\n",cnt,s,1);
modify_DWT(f[cnt],0,1);//要么就不选这个因数
modify_DWT(f[cnt],s,1);//选上这个因数,那么其状态为 s 的那一位也为 1
}else{
int ti=ele[0][i],tl=ele[1][i];
rep(t,0,tl){//枚举这个因数中,第 i 个质因数的次方
if(t==0)dfs(i+1,now,s<<2|2);//如果他没有这个因数,那么他限定下界
else if(t==tl)dfs(i+1,now,s<<2|1);//如果他的因数与 lcm 对应,那么他限定上界
else dfs(i+1,now,s<<2);//十分平凡
if(1ll*now*ti<=n)now*=ti;//保证因数在 n 的范围之内
else return;
}
}
}
然后,我们设 \(f[i][j]\) 表示在前 \(i\) 个数中,选择的因数代码的状态 \(s\) 或起来等于 \(j\) 的方案数,那么就有
俨然一副 \(FWT\) 的样子。
显然,最后我们需要的就是 \(1111...\) 的状态。
可以先将所有的 \(f[i]\) 的 \(DWT\) 形式处理出来,然后对位累乘即可。
但是我们所求的是
在 \([1,n]\) 之间,选一些数,其中必选 \(\frac{X}{G}\),使得他们的 \(\gcd\) 为 \(1\),\(\text{lcm}\) 为 \(n\)。
怎么 “必选 \(\frac{X}{G}\) ”?考虑容斥——将所有方案处理出来,再减去不选 \(\frac{X}{G}\) 的情况。
那么怎么求不选呢?累乘的时候不乘上 \(\frac{X}{G}\) 对应的 \(f[i]\) 即可。
为了快速处理,我们可以求出 \(f[i]\) 的前缀积和后缀积,这样可以 \(\mathcal O(N)\) 算出我们需要的数组。
最后询问我们得到的数组的原形式的最后一项即可(对应状态 \(1111....\) )
但是,这道题有一个优化——因为我们的 \(DWT\) 过程都是 “单点改(初始化 \(f[i]\) 时我们只将 \(f[i][0],f[i][s]\) 赋值为 \(1\) ),单点问(最后我们只想问得到的数组的最后一项)”,那么,我们可以想一下怎么 \(\mathcal O(N)\) 地将我们的单点修改的影响直接加在 \(DWT\) 之后的数组上,怎么 \(\mathcal O(N)\) 地询问我们的最后一个位置的值?
这里不做过多解释(似乎不加这个优化吸了氧才堪堪过),具体对应的部分是代码中的 modify_DWT()
和 getLast()
函数,配套 getLast()
的是 init[]
数组,可以自行钻研一下。
代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
typedef long long LL;
typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long
#ifdef _GLIBCXX_CSTDIO
#define cg (c=getchar())
template<class T>inline void qread(T& x){
char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
template<class T>inline T qread(const T sample){
T x=0;char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
#undef cg
template<class T>void fwrit(const T x){//just short,int and long long
if(x<0)return (void)(putchar('-'),fwrit(-x));
if(x>9)fwrit(x/10);
putchar(x%10^48);
}
#endif
// template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}
const int MOD=1e9+7;
const int inv2=(MOD+1)>>1;
const int MAXSIT=800;
inline int Get(const int x){int n=1;while(n<=x)n<<=1;return n;}
inline void DWT_or(vector<int>&f,const int opt){
//if opt==-1, DWT_or will be IDWT_or
int N=Get((int)f.size()-1);
f.resize(N);
for(int t=2;t<=N;t<<=1)
for(int i=0,p=t>>1;i<N;i+=t)for(int j=i;j<i+p;++j)
f[j+p]=(0ll+f[j+p]+f[j]*opt+MOD)%MOD;
}
inline void DWT_and(vector<int>&f,const LL opt){
int N=Get((int)f.size()-1);
f.resize(N);
for(int t=2;t<=N;t<<=1)
for(int i=0,p=t>>1;i<N;i+=t)for(int j=i;j<i+p;++j)
f[j]=(0ll+f[j]+f[j+p]*opt)%MOD;
}
inline void DWT_xor(vector<int>&f,const LL opt){
int N=Get((int)f.size()-1);//pay attention to this -1
f.resize(N);
int x,y;
for(int t=2;t<=N;t<<=1)
for(int i=0,p=t>>1;i<N;i+=t)for(int j=i;j<i+p;++j){
x=f[j],y=f[j+p];
f[j]=(0ll+x+y)%MOD,f[j+p]=(0ll+x-y+MOD)%MOD;
if(opt==-1)f[j]=1ll*f[j]*inv2%MOD,f[j+p]=1ll*f[j+p]*inv2%MOD;
}
}
int ans[MAXSIT+5],cnt;
int n,g,l,mirl;
int init[1<<16|2];
inline void Init(){
n=qread(1),g=qread(1),mirl=l=qread(1);
if(l%g){
rep(i,1,qread(1))writc(0,'\n');
exit(0);
}else l/=g,n/=g;
}
vector<int>ele[2];
int len,sz;//有多少个质因数,状态总数
//ele[0]:保存分解 l 的质因数
//ele[1]:保存分解 l 的这个因数的次方
vector<pii>lst;//保存每个因数以及其对应编号
inline int findPos(const int pos){
vector<pii>::iterator it=lower_bound(lst.begin(),lst.end(),mp(pos,0));
return it->second;
}
inline void Dec(){
for(int i=2,t=0;i*i<=l;++i,t=0)if(l%i==0){
while(l%i==0)l/=i,++t;
ele[0].push_back(i);
ele[1].push_back(t);
// printf("this part, i == %d, t == %d\n",i,t);
}
if(l!=1)ele[0].push_back(l),ele[1].push_back(1);
// printf("this part, i == %d, t == %d\n",l,1);
}
inline void MakeInit(){
sz=1<<((len=ele[0].size())<<1);
init[sz-1]=1;
for(int i=1;i<sz;i<<=1)for(int j=sz-1;j>=sz-i;--j)
init[j-i]=-init[j];
}
inline void modify_DWT(vector<int>&f,const int pos,const int delta){
int n=Get(f.size()-1);
f.resize(n);
// printf("Now n == %d, pos == %d\n",n,pos);
rep(i,0,n-1){
if((i&pos)==0){
// printf("udpate %d\n",i+pos);
f[i+pos]+=delta;
if(f[i+pos]>=MOD)f[i+pos]-=MOD;
}
}
}
inline int getLast(vector<int>&f){
int ret=0,sz=f.size();
rep(i,0,sz-1){
ret+=f[i]*init[i];
if(ret<0)ret+=MOD;
else if(ret>=MOD)ret-=MOD;
}return ret;
}
vector<int>f[MAXSIT+5];
inline int print(int now){
while(now){
if(now&1)putchar('1');
else putchar('0');
now>>=1;
}
}
void dfs(const int i,int now,const int s){
if(i==len){//枚举了一个因数
ans[++cnt]=-1;//更新因数编号
lst.push_back(mp(now,cnt));//并将其放进所有的因数集合中
// printf("this num,now == %d, cnt == %d, s == ",now,cnt);print(s);Endl;
f[cnt].resize(sz);
// printf("modify_DWT(%d,%d,%d)\n",cnt,0,1);
// printf("modify_DWT(%d,%d,%d)\n",cnt,s,1);
modify_DWT(f[cnt],0,1);//要么就不选这个因数
modify_DWT(f[cnt],s,1);//选上这个因数,那么其状态为 s 的那一位也为 1
}else{
int ti=ele[0][i],tl=ele[1][i];
rep(t,0,tl){//枚举这个因数中,第 i 个质因数的次方
if(t==0)dfs(i+1,now,s<<2|2);//如果他没有这个因数,那么他限定下界
else if(t==tl)dfs(i+1,now,s<<2|1);//如果他的因数与 lcm 对应,那么他限定上界
else dfs(i+1,now,s<<2);//十分平凡
if(1ll*now*ti<=n)now*=ti;//保证因数在 n 的范围之内
else return;
}
}
}
int sum[2][MAXSIT+5][1<<16];
//前缀积和后缀积
vector<int>t;
int all;
inline void preSolve(){
rep(i,0,sz)sum[0][0][i]=sum[1][cnt+1][i]=1;
rep(i,1,cnt)rep(j,0,sz-1)sum[0][i][j]=1ll*sum[0][i-1][j]*f[i][j]%MOD;
fep(i,cnt,1)rep(j,0,sz-1)sum[1][i][j]=1ll*sum[1][i+1][j]*f[i][j]%MOD;
t.resize(sz);
rep(i,0,sz-1)t[i]=sum[0][cnt][i];
all=getLast(t);
// printf("all == %d\n",all);
}
signed main(){
// ios::sync_with_stdio(false);
Init();
Dec();
MakeInit();
dfs(0,1,0);//枚举其所有满足条件的因数
preSolve();
sort(lst.begin(),lst.end());
int pos;
rep(i,1,qread(1)){
pos=qread(1);
if(pos%g || mirl%pos || pos/g>n){
writc(0,'\n');continue;
}
pos=findPos(pos/g);
// printf("pos == %d\n",pos);
if(ans[pos]==-1){
rep(i,0,sz-1)t[i]=1ll*sum[0][pos-1][i]*sum[1][pos+1][i]%MOD;
// printf("getLast() == %d\n",getLast(t));
ans[pos]=all-getLast(t);
if(ans[pos]<0)ans[pos]+=MOD;
else if(ans[pos]>=MOD)ans[pos]-=MOD;
}writc(ans[pos],'\n');
}
return 0;
}