习题:遗失的答案(FWT)
题目
思路
很明显,当\(G \not| L\)的时候无解,
先对最大公因数的条件进行化简,可以将所有的值都除以\(G\),只需要选出来的数互质即可
那么将\(l\)进行质因数分解,有\(l=\prod p_i^{a_i}\),
设选出来的第\(i\)个数可以分解成为\(s_i=\prod p_{i}^{b_{ij}}\)
那么有\(a_i=max\{b_{ij}\}\),可以发现,如果一个位置上已经满足,剩下的可以任意选择
对于\(1e8\)之内的数据,每一个数最多有\(8\)个质因数,也就是指需要\(16\)个位置来进行限制
设$dp[s] \(表示是否满足第\)i$个质因数的条件
对每一个数都处理一下,令\(A_i\)为转换第\(i\)个数之后得到的集合
当然,所转换的数必须是\(L/G\)的因数
那么有\(dp[s]=\sum_{A|B=s} dp[A]dp[B]\),
如果没有\(X\)的限制,这道题就已经解决了,
如果有\(X\)的限制,可以考虑先没有\(X\)将所有的\(dp\)处理出来,在将\(X\)加进去
可以发现,有一些数的\(A\)是相同的,我们将这些\(A\)相同的分到一组里面去,
经过打表,可以发现这样的组不会有很多,大概在\(600\)左右
设\(f[i][j]\)表示前\(i\)组已经达到的状态为\(j\)的方案数,\(g[i][j]\)表示后\(i\)组到达状态\(j\)的方案数,
设\(X\)所在的组为\(index\),那么我们所需要做的就是将\(f[index-1][j]\)和\(g[index+1][j]\)卷起来
之后将\(X\)插入即可,可以发现,如果这样去思考,\(Q=1e5\)实际上和\(Q=600\)是没有区别的
预处理一下单独除去每一组的答案即可,
预处理的复杂度为\(O(600*2^{16}*16)\),询问的复杂度为\(O(Qlog_n)\)
但是如果仔细思考这个过程,将组的顺序合理安排一下,也就是从小到大,那么可以多出很多的空数组,常数也许会比较小。。。
代码
#pragma GCC optimize(2)
#include<unordered_map>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
using namespace std;
namespace IO
{
template<typename T> void read(T &x)
{
x=0;
int f=1;
char c=getchar();
while('0'>c||c>'9')
{
if(c=='-')
f=-1;
c=getchar();
}
while('0'<=c&&c<='9')
{
x=(x<<3)+(x<<1)+c-'0';
c=getchar();
}
x*=f;
}
template<typename T> void write(T x)
{
if(x>9)
write(x/10);
putchar(x%10+'0');
}
}
namespace FWT
{
const int mod=1e9+7;
void fwt(int *a,int l,int r)
{
if(l==r)
return;
int mid=(l+r)>>1;
for(int i=l;i<=mid;i++)
a[i-l+1+mid]=(a[i-l+1+mid]+a[i])%mod;
fwt(a,l,mid);
fwt(a,mid+1,r);
}
void idfwt(int *a,int l,int r)
{
if(l==r)
return;
int mid=(l+r)>>1;
idfwt(a,l,mid);
idfwt(a,mid+1,r);
for(int i=l;i<=mid;i++)
a[i-l+1+mid]=(a[i-l+1+mid]-a[i]+mod)%mod;
}
}
using namespace FWT;
using namespace IO;
int n,g,l,q;
int num[605],st[605],len;//每一组的个数&每一组的状态&有多少个组
int pre[605][(1<<16)+5],suf[605][(1<<16)+5];
int ans[605];//强制选第i组中的某个数的结果
int temp[605][(1<<16)+5]={};
int lenp,lim;
pair<int,int> p[15];
vector<int> fac;
unordered_map<int,int> vis;
int getsta(int x)
{
int ret=0;
for(int i=0;i<lenp;i++)
{
int tot=0;
while(x%p[i].first==0)
{
x/=p[i].first;
tot++;
}
if(tot==0)
ret|=(1<<i);
if(tot==p[i].second)
ret|=(1<<(i+lenp));
}
return ret;
}
long long qkpow(int a,int b)
{
int t=1;
int mul=a;
while(b)
{
if(b&1)
t=t*mul%mod;
mul=mul*mul%mod;
b>>=1;
}
return t;
}
void prepa()
{
pre[0][0]=1;
suf[len+1][0]=1;
int val1,val2;
for(int i=1;i<=len;i++)
{
val1=qkpow(2,num[i])-1;
val2=qkpow(2,num[len-i+1])-1;
for(int j=0;j<lim;j++)
{
pre[i][j]=(pre[i][j]+pre[i-1][j])%mod;
pre[i][j|st[i]]=(pre[i][j|st[i]]+1ll*pre[i-1][j]*val1%mod)%mod;
suf[len-i+1][j]=(suf[len-i+1][j]+suf[len-i+2][j])%mod;
suf[len-i+1][j|st[len-i+1]]=(suf[len-i+1][j|st[len-i+1]]+1ll*suf[len-i+2][j]*val2%mod)%mod;
}
}
fwt(pre[0],0,lim-1);
fwt(suf[len+1],0,lim-1);
for(int i=1;i<=len;i++)
{
fwt(pre[i],0,lim-1);
fwt(suf[i],0,lim-1);
}
for(int i=1;i<=len;i++)
{
for(int j=0;j<lim;j++)
if((j|st[i])==j)
temp[i][j]=1ll*pre[i-1][j]*suf[i+1][j]%mod;
else
temp[i][j]=0;
idfwt(temp[i],0,lim-1);
ans[i]=1ll*temp[i][lim-1]*qkpow(2,num[i]-1)%mod;
}
}
int main()
{
//freopen("missedans6.in","r",stdin);
//freopen("my.out","w",stdout);
read(n);read(g);read(l);
read(q);
if(l%g!=0)
{
for(int i=1;i<=q;i++)
{
write(0);
putchar('\n');
}
return 0;
}
l/=g;n/=g;
int templ=l;
for(int i=2;i<=templ;i++)
{
if(templ%i==0)
{
p[lenp].first=i;
while(templ%i==0)
{
templ/=i;
p[lenp].second++;
}
lenp++;
}
}
/*cout<<"p:";
for(int i=0;i<lenp;i++)
cout<<p[i].first<<' ';
cout<<'\n';*/
for(int i=1;1ll*i*i<=l&&i<=n;i++)
{
if(l%i==0)
{
fac.push_back(i);
if(i*i!=l&&l/i<=n)
fac.push_back(l/i);
}
}
//sort(fac.begin(),fac.end(),cmp);
for(int i=0;i<fac.size();i++)
{
int s=getsta(fac[i]);
//cout<<fac[i]<<"->"<<s<<'\n';
if(vis.count(s)==0)
{
vis[s]=++len;
st[len]=s;
}
num[vis[s]]++;
}
lim=(1<<(2*lenp));
prepa();
for(int i=1,x;i<=q;i++)
{
cin>>x;
if(x%g!=0)
write(0);
else
{
x/=g;
if(l%x!=0||x>n)
write(0);
else
write(ans[vis[getsta(x)]]);
}
putchar('\n');
}
return 0;
}
/*
962 2 4050
94470
405
5
18
*/