【BZOJ5302】【HAOI2018】—奇怪的背包(裴蜀定理+dp)

传送门

题意:求满足x1v1+x2v2+xnvnmod p=wx_1v_1+x_2v_2…+x_nv_nmod\ p=w的个数
只把是否取vv看做2种不同情况

考虑裴蜀定理ax+by+cz...=pax+by+cz...=p有解当且仅当gcd(a,b,c...)pgcd(a,b,c...)|p

%p\%p看做kp-kp,那么显然有解得保证gcd(v1,v2....)pgcd(v1,v2....)|p
因为1e91e9以内因子个数最多也只有1e31e3
那我们可以枚举pp的因数dpdp求方案
f[i][j]f[i][j]表示前ii个因子,凑出第jj个因子的方案数
暴力转移即可

#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline int read(){
	char ch=getchar();
	int res=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=getchar();
	return res*f;
}
int gcd(int a,int b){
	return b==0?a:gcd(b,a%b);
}
const ll mod=1e9+7;
const int N=1000006;
const int M=50004;
int n,p,q;
int v[N],w[N],tot;
ll f[2][M],g[M],d[N],fac[N],ans[M];
inline int find(ll x){
	if(x*x<=p)return d[x];
	return d[p/x]+1;
}
int main(){
	n=read(),q=read(),p=read();
	for(ll i=1;i*i<=p;i++){
		if(p%i==0){
			fac[++tot]=i;
			d[i]=tot;
			if(i*i!=p)fac[++tot]=p/i;
		}
	}
	for(int i=1;i<=n;i++){
		int x=read();
		x=gcd(p,x);
		if(!g[find(x)])g[find(x)]=2;
		else (g[find(x)]*=2)%=mod;
	}
	int now=1,past=0;
	f[past][find(p)]=1;
	for(int i=1;i<=tot;i++){
		memset(f[now],0,sizeof(f[now]));
		if(!g[i])g[i]=1;
		for(int j=1;j<=tot;j++){
			int x=find(gcd(fac[i],fac[j]));
			(f[now][j]+=f[past][j])%=mod;
			(f[now][x]+=f[past][j]*(g[i]-1)%mod)%=mod;
		}
		swap(now,past);
	}
	for(int i=1;i<=tot;i++){
		for(int j=1;j<=tot;j++){
			if(fac[i]%fac[j]==0)(ans[i]+=f[past][j])%=mod;
		}
	}
	for(int i=1;i<=q;i++){
		int x=read();
		x=gcd(x,p);
		if(x!=p)cout<<ans[find(x)]<<'\n';
		else cout<<ans[find(x)]-1<<'\n';
	}
}
posted @ 2019-02-28 09:08  Stargazer_cykoi  阅读(123)  评论(0编辑  收藏  举报