1. 程式人生 > 其它 >P3455 [POI2007]ZAP-Queris(莫比烏斯反演)

P3455 [POI2007]ZAP-Queris(莫比烏斯反演)

題意:

給出\(a,b,d\),求滿足\(1 \leq x \leq a,1 \leq y \leq b\),且\(gcd(x,y)=d\)的二元組\((x,y)\)的數量。

\(1 \leq n \leq 5 \times 10^4\)

\(1 \leq d \leq a,b \leq 5 \times 10^4\)

題解:

寫出式子:

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

可以把\(d\)提取出來,變成

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

可以把\(gcd(i,j)=1\)變成\(\sum_{d|gcd(i,j)}\mu(d)\)

(莫比烏斯函式的性質),變成

\(\sum_{i=1}^{a/k}\sum_{j=1}^{b/k}\sum_{d|gcd(i,j)}\mu(d)\)

\(i,j\)都是\(d\)的倍數,且\(d\)的範圍一定是\(min(a,b)\)

這裡預設\(a\)是較小的那個,

所以可以把\(d\)移到前面,變成

\(\sum_{d=1}^a\mu(d)[n/kd][m/kd]\)

然後套一個二維的數論分塊,時間複雜度\(O(\sqrt{n})\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
typedef long long ll;
int vis[maxn];
ll pri[maxn],mu[maxn],sum[maxn],cnt;
void getMu (ll n) {
	//線性篩莫比烏斯函式,並求解字首和 
	mu[1]=1;
	for (ll i=2;i<=n;i++) {
		if (!vis[i]) {
			mu[i]=-1;
			pri[++cnt]=i;
		}
		for (ll j=1;j<=cnt&&i*pri[j]<=n;j++) {
			vis[i*pri[j]]=1;
			if (i%pri[j]==0) break;
			else mu[i*pri[j]]=-mu[i];
		}
	}
	for (ll i=1;i<=n;i++) sum[i]=sum[i-1]+mu[i];
}
int main () {
	int _;
	scanf("%d",&_);
	getMu(60000);
	while (_--) {
		ll A,B,D;
		scanf("%lld%lld%lld",&A,&B,&D);
		if (A>B) swap(A,B);
		ll ans=0;
		for (ll l=1,r;l<=A;l=r+1) {
			r=min(A/(A/l),B/(B/l));
			ans+=(A/(l*D))*(B/(l*D))*(sum[r]-sum[l-1]);
		}
		printf("%lld\n",ans);
	}
}