luogu P3455【[POI2007]ZAP-Queries】

此题为套路题

前置芝士

数论分块,莫比乌斯反演

做法

首先可以看出题目求

\[\sum_{i=1}^{a} \sum_{j=1}^{b}[gcd(i,j)=d] \]

首先很套路地转化为

\[\sum_{i=1}^{\left \lfloor \frac{a}{d} \right \rfloor}\sum_{i=1}^{\left \lfloor \frac{b}{d} \right \rfloor}[gcd(i,j)=1] \]

有一个数论函数 \(\varepsilon(x)\),当\(x=1\)时,函数值为\(1\)否则为\(0\)

后面的\([gcd(i,j)=1]\)可以用\(\varepsilon(gcd(i,j))\)

\[\sum_{i=1}^{\left \lfloor \frac{a}{d} \right \rfloor}\sum_{i=1}^{\left \lfloor \frac{b}{d} \right \rfloor}\varepsilon(gcd(i,j)) \]

\(\mu*1 = \varepsilon\) 反演

\[\sum_{i=1}^{\left \lfloor \frac{a}{d} \right \rfloor}\sum_{i=1}^{\left \lfloor \frac{b}{d} \right \rfloor}\sum_{p|gcd(i,j)}\mu(p) \]

改变求和顺序

\[\sum_{p=1}\mu(p)\sum_{i=1}^{\left \lfloor \frac{a}{d} \right\rfloor}[p \ | \ i]\sum_{j=1}^{\left \lfloor \frac{b}{d} \right \rfloor}[p \ |\ j] \]

后面两个\(\sum\),可以看出求的是\(d\)有多少个倍数在\(\left \lfloor \frac{a}{d} \right\rfloor\)\(\left \lfloor \frac{b}{d} \right \rfloor\)中。显然,答案分别为\(\left \lfloor \frac{a}{pd} \right\rfloor\)
\(\left \lfloor \frac{b}{pd} \right\rfloor\)

转化为

\[\sum_{p=1}\mu(p)\left \lfloor \frac{a}{pd} \right\rfloor\left \lfloor \frac{b}{pd} \right\rfloor \]

然后数论分块加前缀和就做完了

我Code里的是先把\(a/d,b/d\),再做数论分块,这样常数会小一点。

Code

#include 
#include 
#include 
using namespace std;
#define int long long 
inline int read(){
	register int x=0,f=0,ch=getchar();
	while('0'>ch||ch>'9')f^=ch=='-',ch=getchar();
	while('0'<=ch&&ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar();
	return f?-x:x;
}
const int MAX=50100;
int mu[MAX],pri[MAX],f[MAX],tot;
inline void Getmu(int N){
	mu[1]=1;
	for(register int i=2;i<=N;++i){
		if(!f[i]){
			mu[i]=-1;
			pri[++tot]=i;
		} 
		for(register int j=1;j<=tot&&i*pri[j]<=N;++j){
			f[i*pri[j]]=1;
			if(i%pri[j]==0)break;
			mu[i*pri[j]]=-mu[i];
		}
	}
	for(register int i=1;i<=N;++i)mu[i]+=mu[i-1];
} 
int ans;
inline int solve(int n,int m){
	ans=0;
	if(n>m)n^=m^=n^=m;
	for(register int l=1,r;l<=n;l=r+1){
		r=min(n/(n/l),m/(m/l));
		ans+=(mu[r]-mu[l-1])*(n/l)*(m/l);
	}
	return ans;
}
int t,a,b,d;
signed main(){
	Getmu(50000);
	t=read();
	while(t--){
		a=read(),b=read(),d=read();
		a/=d,b/=d;
		printf("%lld\n",solve(a,b));
	}
	return 0;
}


你可能感兴趣的:(luogu P3455【[POI2007]ZAP-Queries】)