BZOJ 5302: [Haoi2018]奇怪的背包
首先根据裴蜀定理,v[i]等价于gcd(v[i],P),w[i]等价于gcd(w[i],P),所以我们只要求出P的约数的答案,F[i][j]表示用了前i种约数,能产生最小值为第j种约数的方案数,统计答案即可
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
const int mod=1e9+7;
int n,q,P,top,vec[1000005],ans[1000005],Sum[1000005],V[1000005],F[2005][2005];
map<int,int> M;
int gcd(int a,int b){
if (b==0) return a;
return gcd(b,a%b);
}
int main(){
scanf("%d%d%d",&n,&q,&P);
for (int i=1; i*i<=P; i++)
if (P%i==0){
vec[++top]=i;
M[i]=top;
if (i*i!=P) {
vec[++top]=P/i;
M[P/i]=top;
}
}
for (int i=1; i<=top; i++) Sum[i]=1;
for (int i=1; i<=n; i++) {
scanf("%d",&V[i]);
V[i]=gcd(V[i],P);
(Sum[M[V[i]]]<<=1)%=mod;
}
F[0][M[P]]=1;
for (int i=0; i<top; i++)
for (int j=1; j<=top; j++){
int x=M[gcd(vec[i+1],vec[j])];
(F[i+1][j]+=F[i][j])%=mod;
(F[i+1][x]+=1ll*F[i][j]*(Sum[i+1]-1)%mod)%=mod;
}
for (int i=1; i<=top; i++)
for (int j=1; j<=top; j++)
if (vec[i]%vec[j]==0) (ans[i]+=F[top][j])%=mod;
while (q--){
int x;
scanf("%d",&x);
x=gcd(x,P);
int ANS=ans[M[x]];
if (x==P) ANS--;
(ANS+=mod)%=mod;
printf("%d\n",ANS);
}
return 0;
}

浙公网安备 33010602011771号