其他分享
首页 > 其他分享> > 2019.02.28【HAOI2018】【BZOJ5302】【洛谷P4495】奇怪的背包(裴蜀定理)

2019.02.28【HAOI2018】【BZOJ5302】【洛谷P4495】奇怪的背包(裴蜀定理)

作者:互联网

BZOJ传送门

洛谷传送门


解析:

熟悉exgcdexgcdexgcd的人都知道这道题的结论。。。

每一个viv_ivi​等价于gcd(vi,P)gcd(v_i,P)gcd(vi​,P)

最后要求的www在模意义下实际上就是gcd(w,P)gcd(w,P)gcd(w,P)

O(P)O(\sqrt P)O(P​)时间处理出所有PPP的约数就能愉快地水过了

当然还是讲一下怎么做。

以下的viv_ivi​和www均是取了gcdgcdgcd之后的。

由裴蜀定理,最后选择的viv_ivi​要能够凑出www,需要满足gcdi=1t(vi)w\gcd\limits_{i=1}^t(v_i)\mid wi=1gcdt​(vi​)∣w。

所以只需要预处理出最终gcdgcdgcd等于每个约数的方案数就行了。

同时统计一下每个约数的因数前缀和

预处理复杂度大概是O(σ(P)2)=O(P23)O(\sigma(P)^2)=O(P^{\frac{2}{3}})O(σ(P)2)=O(P32​)

然后就可以O(1)O(1)O(1)回答每个询问了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

struct Map{
	cs static int magic=189859;
	int key[magic],val[magic];
	Map(){memset(key,-1,sizeof key);};
	
	cs int &operator[](cs int &k)cs{
		int h=k%magic;
		while((~key[h])&&(key[h]^k))h=(h+1)%magic;
		return val[h];
	}
	
	int &operator[](cs int &k){
		int h=k%magic;
		while((~key[h])&&(key[h]^k))h=(h+1)%magic;
		if(key[h]^k)key[h]=k;
		return val[h];
	}
}id;

cs int mod=1e9+7;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a<b?a-b+mod:a-b;}
inline int mul(int a,int b){return (ll)a*b%mod;}

cs int N=1e6+6,M=1e5+5;
int n,q,P;
int v[N];
int factor[M],fcnt,cnt[M];
int f[M],now;
int ans[M];
int pow2[N],sum[N];

inline void sieve(){
	for(int re i=1;(ll)i*i<=P;++i)if(P%i==0)factor[id[i]=++fcnt]=i;
	for(int re i=fcnt;i;--i)if(P/factor[i]!=factor[i])factor[id[P/factor[i]]=++fcnt]=P/factor[i];
}

inline int gcd(int a,int b){
	if(!a||!b)return a|b;
	re int shift=__builtin_ctz(a|b);
	for(b>>=__builtin_ctz(b);a;a-=b)if((a>>=__builtin_ctz(a))<b)swap(a,b);
	return b<<shift;
}

inline void init(){
	for(int re i=1;i<=n;++i)++cnt[id[gcd(v[i],P)]];
	pow2[0]=1;
	for(int re i=1;i<=n;++i)pow2[i]=mul(2,pow2[i-1]);
	for(int re i=1;i<=n;++i)pow2[i]=dec(pow2[i],1);
	for(int re i=1;i<=fcnt;++i)if(cnt[i]){
		for(int re j=1;j<=fcnt;++j)if(f[j]){
			int nxt=gcd(factor[i],factor[j]);
			int pos=id[nxt];
			f[pos]=add(f[pos],mul(f[j],pow2[cnt[i]]));
		}
		f[i]=add(f[i],pow2[cnt[i]]);
	}
	for(int re i=fcnt;i;--i)
	for(int re j=i-1;j;--j)if(factor[i]%factor[j]==0)f[i]=add(f[i],f[j]);
}

signed main(){
	n=getint();q=getint();P=getint();
	sieve();
	for(int re i=1;i<=n;++i)v[i]=getint();
	init();
	while(q--)cout<<f[id[gcd(getint(),P)]]<<"\n";
	return 0;
}

标签:约数,www,P4495,洛谷,gcd,int,vi,2019.02,define
来源: https://blog.csdn.net/zxyoi_dreamer/article/details/88045837