Loading [MathJax]/jax/element/mml/optable/GeneralPunctuation.js
/* 返回顶部 */

Luogu P1390 公约数的和

gate
求:

ni=1mj=1gcd(i,j)

gcd(i,j)=k,枚举k

nk=1kni=1mj=1[gcd(i,j)=k]

同时除以k

nk=1knki=1mkj=1[gcd(i,j)=1]

根据
\begin{array} \ \because \sum\limits_{d∣n}\mu(d)=[n=1] \\ \therefore \sum\limits_{d∣gcd(i,j)}​μ(d) = [gcd(i,j)=1] \end{array}
[gcd(i,j)=1]替换掉

\sum\limits_{k=1}^n k \sum\limits_{i=1}^{\lfloor \frac{n}{k}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{m}{k}\rfloor} \sum\limits_{d∣n}\mu(d)

d提前,同时把i,j除以d

\sum\limits_{k=1}^n k\sum\limits_{d=1}^{\lfloor \frac{n}{k}\rfloor}\sum\limits_{i=1}^{\lfloor \frac{n}{kd}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{m}{kd}\rfloor}\mu(d)

整除分块

\sum\limits_{k=1}^n k\sum\limits_{d=1}^{\lfloor \frac{n}{k}\rfloor}\mu(d)\lfloor \frac{n}{kd}\rfloor\lfloor \frac{m}{kd}\rfloor

T=kd

\sum\limits_{k=1}^n k\sum\limits_{d=1}^{\lfloor \frac{n}{k}\rfloor}\mu(d)\lfloor \frac{n}{T}\rfloor\lfloor \frac{m}{T}\rfloor

d换成T

\sum\limits_{T=1}^n\sum\limits_{k|T} k\mu(\frac{T}{k})\lfloor \frac{n}{T}\rfloor\lfloor \frac{m}{T}\rfloor

k也就是id(k),可以写成

\sum\limits_{T=1}^n\sum\limits_{k|T} id(k)\mu(\frac{T}{k})\lfloor \frac{n}{T}\rfloor\lfloor \frac{m}{T}\rfloor

因为k*\frac{T}{k} = T,所以在枚举T时,k\frac{T}{k}可以看成相同的元素,只是顺序相反
\begin{array} \ \because id*\mu=\varphi \\ \therefore \sum\limits_{k|T}id(k)\mu(\frac{T}{k}) = \sum\limits_{k|T}\varphi(k) \end{array}
\because \varphi是积性函数
\therefore \sum\limits_{k|T}\varphi(k) = \varphi(T)
即原式可以转化为

\sum\limits_{T=1}^n\varphi(T)\lfloor \frac{n}{T}\rfloor\lfloor \frac{m}{T}\rfloor


莫比乌斯反演的部分到此结束了,但注意,题目要求的是[1,n]中每任意两个不同的数的gcd之和,
而上述做法多加了gcd(i,i),且重复计算了gcd(i,j)gcd(j,i)
所以我们要把多余的部分减去,最终答案即 (Ans-\sum\limits_{i=1}^ni)/2
也就是等差数列,可以写成(Ans-(1+n)*n/2)/2

代码如下

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define MogeKo qwq
using namespace std;

const int maxn = 2e6+5;
long long n,p[maxn];

void get_phi(long long n) {
	for(long long i = 1; i <= n; i++)
		p[i] = i;
	for(long long i = 2; i <= n; i++)
		if(p[i] == i)
			for(long long j = i; j <= n; j += i)
				p[j] = p[j]/i*(i-1);
	for(long long i = 1; i <= n; i++)
		p[i] += p[i-1];
}

long long calc(long long n,long long m) {
	long long ans = 0;
	long long r = 0;
	for(long long i = 1; i <= n; i = r+1) {
		r = min(n/(n/i),m/(m/i));
		ans += (p[r]-p[i-1]) * (n/i) * (m/i);
	}
	return ans;
}

int main() {
	scanf("%lld",&n);
	get_phi(n);
	printf("%lld",(calc(n,n)-(1+n)*n/2)/2);
	return 0;
}
posted @   Mogeko  阅读(139)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示