洛谷 P2257 YY的GCD(莫比乌斯反演)

Description:

神犇YY虐完数论后给傻×kAc出了一题

给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对

kAc这种傻×必然不会了,于是向你来请教……

多组输入

Input:

第一行一个整数T 表述数据组数

接下来T行,每行两个正整数,表示N, M

T = 10000

N, M <= 10000000

Output

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

Sample Input:

2
10 10
100 100

Sample Output:

30
2791

题目链接

题意为求出 T T T i = 1 n j = 1 m [ g c d ( i , j ) p r i m e ] \sum\limits_{i=1}^{n} \sum\limits_{j=1}^{m}[gcd(i, j) \in prime] i=1nj=1m[gcd(i,j)prime]

首先我们令 n &lt; m n &lt; m n<m ,之后对此式进行化简,将 g c d ( i , j ) gcd(i,j) gcd(i,j) 设为变量 p p p

<munderover> p = 1 n </munderover> <munderover> i = 1 n </munderover> <munderover> j = 1 m </munderover> [ g c d ( i , j ) = p ] ( p p r i m e ) \sum\limits_{p=1}^{n}\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{m} [gcd(i, j)=p] (p\in prime) p=1ni=1nj=1m[gcd(i,j)=p](pprime)

化简得

<munderover> p = 1 n </munderover> <munderover> i = 1 n p </munderover> <munderover> j = 1 m p </munderover> [ g c d ( i , j ) = 1 ] \sum\limits_{p=1}^{n}\sum\limits_{i=1}^{\lfloor \frac{n}{p} \rfloor} \sum\limits_{j=1}^{\lfloor \frac{m}{p} \rfloor}[gcd(i, j)=1] p=1ni=1pnj=1pm[gcd(i,j)=1]

利用莫比乌斯函数性质可得

<munderover> p = 1 n </munderover> <munderover> i = 1 n p </munderover> <munderover> j = 1 m p </munderover> <munder> d g c d ( i , j ) </munder> μ ( d ) \sum\limits_{p=1}^{n}\sum\limits_{i=1}^{\lfloor \frac{n}{p} \rfloor} \sum\limits_{j=1}^{\lfloor \frac{m}{p} \rfloor} \sum\limits_{d|gcd(i, j)} \mu (d) p=1ni=1pnj=1pmdgcd(i,j)μ(d)

考虑枚举 d d d

<munderover> p = 1 n </munderover> <munderover> d = 1 n p </munderover> μ ( d ) <munderover> i = 1 n p </munderover> [ d i ] <munderover> j = 1 m p </munderover> [ d j ] \sum\limits_{p=1}^{n}\sum\limits_{d=1}^{\lfloor \frac{n}{p} \rfloor} \mu (d) \sum\limits_{i=1}^{\lfloor \frac{n}{p} \rfloor}[d|i] \sum\limits_{j=1}^{\lfloor \frac{m}{p} \rfloor}[d|j]​ p=1nd=1pnμ(d)i=1pn[di]j=1pm[dj]

其中 i = 1 n p [ d i ] \sum\limits_{i=1}^{\lfloor \frac{n}{p} \rfloor}[d|i] i=1pn[di] [ 1 , n p ] [1,\lfloor \frac{n}{p} \rfloor] [1,pn] d d d 的倍数, j = 1 m p [ d j ] \sum\limits_{j=1}^{\lfloor \frac{m}{p} \rfloor}[d|j] j=1pm[dj] [ 1 , m p ] [1,\lfloor \frac{m}{p} \rfloor] [1,pm] d d d 的倍数,两项可化简为 n p d \lfloor \frac{n}{pd} \rfloor pdn m p d \lfloor \frac{m}{pd} \rfloor pdm

这样可以推导出 i = 1 n j = 1 m [ g c d ( i , j ) p r i m e ] = d = 1 n p μ ( d ) n p d m p d ( p p r i m e , n &lt; m ) \sum\limits_{i=1}^{n} \sum\limits_{j=1}^{m}[gcd(i, j) \in prime]=\sum\limits_{d=1}^{\lfloor \frac{n}{p} \rfloor} \mu (d) \lfloor \frac{n}{pd} \rfloor \lfloor \frac{m}{pd} \rfloor(p \in prime,n&lt;m) i=1nj=1m[gcd(i,j)prime]=d=1pnμ(d)pdnpdm(pprime,n<m)

其中莫比乌斯函数 μ \mu μ 可以用线性筛筛出,但是这样 O ( n ) O(n) O(n) 枚举 d d d 的计算复杂度在多组输入中还不是正确的复杂度

考虑继续化简该式,令 T = p d T=pd T=pd ,代入得

<munderover> T = 1 n </munderover> <munder> p T </munder> μ ( T p ) n T m T \sum\limits_{T=1}^{n} \sum\limits_{p|T} \mu (\frac{T}{p}) \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor T=1npTμ(pT)TnTm

整理得

<munderover> T = 1 n </munderover> n T m T <munder> p T </munder> μ ( T p ) \sum\limits_{T=1}^{n} \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor \sum\limits_{p|T} \mu (\frac{T}{p}) T=1nTnTmpTμ(pT)

发现其中 p T μ ( T p ) \sum\limits_{p|T} \mu (\frac{T}{p}) pTμ(pT) 部分可以做前缀和预处理,另外 n T m T \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor TnTm 部分使用分块优化就可以愉(jian)快(nan)地 A C AC AC 掉这道题目啦

AC代码:

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

const int maxn = (int)(1e7 + 5);

bool IsPrime[maxn];
int Tot;
int Prime[maxn];
int Mu[maxn];
long long F[maxn];
long long Prefix[maxn];

void Moblus() {
	for (int i = 0; i < maxn; ++i) IsPrime[i] = true;
	Mu[1] = 1;
	for (int i = 2; i < maxn; ++i) {
		if (IsPrime[i]) {
			Prime[Tot++] = i;
			Mu[i] = -1;
		}
		for (int j = 0; j < Tot && Prime[j] * i < maxn; ++j) {
			IsPrime[i * Prime[j]] = false;
			if (i % Prime[j] == 0) {
				Mu[i * Prime[j]] = 0;
				break;
			}
			Mu[i * Prime[j]] = -Mu[i];
		}
	}
	for (int i = 0; i < Tot; ++i) {
		for (int j = 1; Prime[i] * j < maxn; ++j) {
			F[j * Prime[i]] += Mu[j];
		}
	}
	for (int i = 1; i < maxn; ++i) Prefix[i] = Prefix[i - 1] + F[i];
}

long long Solve(int N, int M) {
	if (N > M) swap(N, M);
	long long Res = 0;
	for (int t = 1, tp; t <= N; t = tp + 1) {
		tp = min(N / (N / t), M / (M / t));
		Res += (Prefix[tp] - Prefix[t - 1]) * (N / t) * (M / t);
	}
	return Res;
}

int T;
int N, M;

int main(int argc, char *argv[]) {
	Moblus();
	scanf("%d", &T);
	for (int Case = 1; Case <= T; ++Case) {
		scanf("%d%d", &N, &M);
		printf("%lld\n", Solve(N, M));
	}
	return 0;
}
全部评论

相关推荐

头像
11-09 12:17
清华大学 C++
out11Man:小丑罢了,不用理会
点赞 评论 收藏
分享
10-09 09:39
门头沟学院 C++
HHHHaos:这也太虚了,工资就一半是真的
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务