P5366-[SNOI2017]遗失的答案【状压dp,FWT】

1|0正题

题目链接:https://www.luogu.com.cn/problem/P5366


1|1题目大意

给出一个n,G,L

q次询问在1n中选择若干个数字并且数字x必选,要求这些数的gcdGlcmL的方案数。

1n,G,L,x108,1q105


1|2解题思路

我们令m=LG,x=xG,n=nG,那么就是求选1m中的数的情况下gcd=1,lcm=m的方案。

发现对于每个m的分解后的质因数pc,我们选择的数的为pk,那么我们至少需要一个k=0和一个k=c

也就是其实0<k<m的都是不会对这个质因数产生影响的,所以我们可以把一个质因子p分出两个状态,分别是k=0的和k=m的有没有。

同样的,我们用上面的状态S表示1n的数字,会发现S最多只有六百出头种不同的取值。

那么我们把这些取值拿出来,把数字分成不同的类,这样我们就只需要考虑每个类的数字个数了。记fi,S表示做完了前i类时状态为S的方案,同理gi,S则表示做完了后i类。

这样我们可以用FWT快速处理出hi,S表示处理了除了第i类以外的所有类,状态为S时的方案。

然后再考虑这一类有一个数字必选来转移每个hi,S就知道每一类的答案了。


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cctype> using namespace std; const int N=1<<16,M=610,P=1e9+7; int n,G,L,q,m,tot,cnt,MS,num[N]; int p[99],c[99],pos[N],pw[M],rev[M]; int f[M][N],g[M][N],h[M][N]; int read(){ int x=0,f=1;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-f;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+c-48;c=getchar();} return x*f; } int power(int x,int b){ int ans=1; while(b){ if(b&1)ans=1ll*ans*x%P; x=1ll*x*x%P;b>>=1; } return ans; } void FWT(int *f,int n,int op){ for(int p=2;p<=n;p<<=1) for(int k=0,len=p>>1;k<n;k+=p) for(int i=k;i<k+len;i++) (f[i+len]+=f[i]*op)%=P; return; } void dfs(int dep,int x,int s){ if(x>n)return; if(dep>cnt){num[s]++;return;} dfs(dep+1,x,s|(1<<dep+cnt-1)); for(int i=1,pw=1;i<=c[dep];i++) pw=pw*p[dep],dfs(dep+1,x*pw,s|((i==c[dep])<<dep-1)); return; } void init(){ int x=m; for(int i=2;i<=x;i++) if(x%i==0){ p[++cnt]=i; while(x%i==0)c[cnt]++,x/=i; } dfs(1,1,0);MS=(1<<cnt*2); for(int i=0;i<MS;i++) if(num[i]){ pos[i]=++tot;rev[tot]=i; pw[tot]=power(2,num[i])-1; } f[0][0]=g[tot+1][0]=1; for(int i=1;i<=tot;i++) for(int j=0;j<MS;j++){ (f[i][j]+=f[i-1][j])%=P; (f[i][j|rev[i]]+=1ll*f[i-1][j]*pw[i]%P)%=P; } for(int i=tot;i>=1;i--) for(int j=0;j<MS;j++){ (g[i][j]+=g[i+1][j])%=P; (g[i][j|rev[i]]+=1ll*g[i+1][j]*pw[i]%P)%=P; } for(int i=0;i<=tot;i++)FWT(f[i],MS,1); for(int i=1;i<=tot+1;i++)FWT(g[i],MS,1); for(int i=1;i<=tot;i++) for(int j=0;j<MS;j++) h[i][j]=1ll*f[i-1][j]*g[i+1][j]%P; for(int i=1;i<=tot;i++){ FWT(h[i],MS,-1); int k=power(2,num[rev[i]]-1); for(int j=MS-1;j>=0;j--){ int r=h[i][j];h[i][j]=0; (h[i][j|rev[i]]+=1ll*r*k%P)%=P; } } return; } signed main() { n=read();G=read();L=read();q=read(); n/=G;m=L/G;init(); while(q--){ int x=read(); if(x%G){puts("0");continue;} if(L%x){puts("0");continue;} x/=G; if(x>n){puts("0");continue;} int S=((1<<cnt)-1)*(1<<cnt); for(int i=1;i<=cnt;i++) if(x%p[i]==0){ int k=0;S^=(1<<i+cnt-1); while(x%p[i]==0)x/=p[i],k++; if(k==c[i])S|=(1<<i-1); } cout<<(h[pos[S]][MS-1]+P)%P<<'\n'; } return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/16191690.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(44)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示