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; }