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
",ANS);
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/silenty/p/9788397.html