题解 P4495 【[HAOI2018]奇怪的背包】
Bruteforces · · 题解
题解
我们首先考虑 通过打表可以发现,对于体积为
所以我们可以把这个物品的体积看做
由此我们得到启发,
继续打表我们又可以发现,如果选取了
我们可以口胡证明,当选取约数个数大于 2 时,上式依然成立。
由此我们可以写出DP方程,设
其中
该部分时间复杂度为
这个时候,对于每一个询问
但是询问数量可以达到
事实上我们可以发现,如果一个数既是
而这可以在DP之后就预处理出来:
因此我们可以
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=1000010,M=10050,mod=1000000007;
int n,q,P,v[N],num[M],tot[M],cnt=0,f[2][M],g[M],now=0,sum[N];
inline void init(){
sum[1]=2;
for(register int i=2;i<=n;i++)sum[i]=sum[i-1]*2%mod;
for(register int i=1;i<=n;i++)sum[i]=(sum[i]+mod-1)%mod;
for(register int i=1;i<=sqrt(P);i++)if(P%i==0)num[++cnt]=i;
for(register int i=cnt;i>1;i--)if(P/num[i]!=num[i])num[++cnt]=P/num[i];
for(register int i=1;i<=n;i++){
int pos=lower_bound(num+1,num+cnt+1,v[i])-num;
tot[pos]++;
}
for(register int i=1;i<=cnt;i++)if(tot[i]){
now^=1;
for(register int j=1;j<=cnt;j++)f[now][j]=f[now^1][j];
for(register int j=1;j<=cnt;j++)if(f[now^1][j]){
int nxt=__gcd(num[j],num[i]);
int pos=lower_bound(num+1,num+cnt+1,nxt)-num;
(f[now][pos]+=1LL*f[now^1][j]*sum[tot[i]]%mod)%=mod;
}
(f[now][i]+=sum[tot[i]])%=mod;
}
for(register int i=1;i<=cnt;i++){
for(register int j=1;j<=i;j++)if(num[i]%num[j]==0){
(g[i]+=f[now][j])%=mod;
}
}
}
int main(){
scanf("%d%d%d",&n,&q,&P);
for(register int i=1;i<=n;i++)scanf("%d",&v[i]),v[i]=__gcd(v[i],P);
init();
for(register int i=1;i<=q;i++){
int x,ans=0;scanf("%d",&x);x=__gcd(x,P);
int pos=lower_bound(num+1,num+cnt+1,x)-num;
printf("%d\n",g[pos]);
}
return 0;
}