首页 > 技术文章 > [bzoj2820] YY的GCD

hbyer 2018-12-01 21:32 原文

Description

神犇YY虐完数论后给傻×kAc出了一题给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对kAc这种

傻×必然不会了,于是向你来请教……多组输入

Input

第一行一个整数T 表述数据组数接下来T行,每行两个正整数,表示N, M

Output

T行,每行一个整数表示第i组数据的结果

Sample Input

2
10 10
100 100

Sample Output

30
2791

HINT

T = 10000

N, M <= 10000000

Source

题解

前置知识:莫比乌斯反演

题目让我们求这个:

\[ans=\sum_{i=1}^n\sum_{j=1}^m [gcd(i,j) \in pri]\\ \]

其中\(pri\)表示质数集合。

然后枚举这个质数:

\[\begin {align} ans&= \sum _{d\in pri} \sum _{i=1}^{n} \sum _{j=1}^{m} [gcd(i,j)=d]\\ &=\sum _{d\in pri} \sum _{i=1}^{\lfloor \frac{n}{d} \rfloor} \sum _{j=1}^{\lfloor \frac{m}{d} \rfloor} [gcd(i,j)=1] \\ \end {align} \]

根据莫比乌斯函数的性质可以得到:

\[\sum _{d ^\prime|n} \mu(d ^\prime)=[n=1] \]

然后把\(n\)换成\(gcd(i,j)\)带进去,得:

\[ans=\sum _{d\in pri} \sum _{i=1}^{\lfloor \frac{n}{d} \rfloor} \sum _{j=1}^{\lfloor \frac{m}{d} \rfloor} \sum _{d^\prime|i \&d^\prime|j} \mu (d^\prime) \]

然后先枚举\(d^\prime\),得:

\[\begin{align} ans&=\sum _{d\in pri} \sum _{d^\prime} \mu (d^\prime) \sum _{i=1}^{\lfloor \frac{n}{dd^\prime} \rfloor} \sum _{j=1}^{\lfloor \frac{m}{dd^\prime} \rfloor} 1\\ ans&= \sum _{d\in pri} \sum _{d^\prime} \mu(d^\prime) \lfloor \frac{n}{dd^\prime}\rfloor \lfloor\frac{m}{dd^\prime}\rfloor \end {align} \]

\(T=dd^\prime\),得:

\[\begin{align} ans&= \sum _{d \in pri } \sum _{T}\mu(\frac{T}{d}) \lfloor \frac{n}{T}\rfloor \lfloor\frac{m}{T}\rfloor \\ &= \sum _{T}\lfloor \frac{n}{T}\rfloor \lfloor\frac{m}{T}\rfloor \sum _{d \in pri \& d|T} \mu(\frac{T}{d}) \end {align} \]

\[\begin {align} &\text{令}f(n) = \sum _{d\in pri \& d|T} \mu (d) \\ &ans= \sum _{T}\lfloor \frac{n}{T}\rfloor \lfloor\frac{m}{T}\rfloor f(T) \end {align} \]

线筛出质数和\(\mu\)函数之后,暴力预处理\(f\)并求出前缀和,然后数论分块就行了。

由于n以内的质数只有\(O(n/ln(n))\)个,所以预处理复杂度为\(O(n/ln(n)*ln(n/ln(n))+n)=O(n)\),具体参考调和级数。

然后询问的复杂度为\(O(q\sqrt{n})\)

#include<bits/stdc++.h>
using namespace std;

#define int long long 

void read(int &x) {
	x=0;int f=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
	for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

void print(int x) {
	if(x<0) putchar('-'),x=-x;
	if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

const int maxn = 1e7+1;

int mu[maxn+10],sum[maxn+10],pri[maxn+10],tot,vis[maxn+10],n,m;

void time() {cerr << (double) clock()/CLOCKS_PER_SEC << endl;}

void sieve() {
	mu[1]=1;
	for(int i=2;i<maxn;i++) {
		if(!vis[i]) pri[++tot]=i,mu[i]=-1;
		for(int j=1;j<=tot&&i*pri[j]<maxn;j++) {
			vis[i*pri[j]]=1;
			if(!(i%pri[j])) {mu[i*pri[j]]=0;break;}
			mu[i*pri[j]]=-mu[i];
		}
	}//time();write(tot);
	for(int i=1;i<=tot;i++)
		for(int j=1;j*pri[i]<maxn;j++) sum[j*pri[i]]+=mu[j];
	for(int i=1;i<maxn;i++) sum[i]=sum[i]+sum[i-1];
	//time();
}

signed main() {
	sieve();int asd;read(asd);
	//for(int i=1;i<=10;i++) write(sum[i]);
	while(asd--) {
		read(n),read(m);
		if(n>m) swap(n,m);
		int T=1,ans=0;
		while(T<=n) {
			int pre=T;T=min(n/(n/T),m/(m/T));
			ans+=(n/pre)*(m/pre)*(sum[T]-sum[pre-1]);
			T++;
		}
		write(ans);
	}
	return 0;
}

推荐阅读