gate
求:
[sumlimits_{i=1}^nsumlimits_{j=1}^m gcd(i,j)
]
设(gcd(i,j) = k),枚举(k)
[sumlimits_{k=1}^n k sumlimits_{i=1}^nsumlimits_{j=1}^m [gcd(i,j)=k]
]
同时除以(k)
[sumlimits_{k=1}^n k sumlimits_{i=1}^{lfloor frac{n}{k}
floor}sumlimits_{j=1}^{lfloor frac{m}{k}
floor} [gcd(i,j)=1]
]
根据
(egin{array} ecause sumlimits_{d∣n}mu(d)=[n=1] \ herefore sumlimits_{d∣gcd(i,j)}μ(d) = [gcd(i,j)=1] end{array})
把([gcd(i,j)=1])替换掉
[sumlimits_{k=1}^n k sumlimits_{i=1}^{lfloor frac{n}{k}
floor}sumlimits_{j=1}^{lfloor frac{m}{k}
floor} sumlimits_{d∣n}mu(d)
]
将(d)提前,同时把(i,j)除以(d)
[sumlimits_{k=1}^n ksumlimits_{d=1}^{lfloor frac{n}{k}
floor}sumlimits_{i=1}^{lfloor frac{n}{kd}
floor}sumlimits_{j=1}^{lfloor frac{m}{kd}
floor}mu(d)
]
整除分块
[sumlimits_{k=1}^n ksumlimits_{d=1}^{lfloor frac{n}{k}
floor}mu(d)lfloor frac{n}{kd}
floorlfloor frac{m}{kd}
floor
]
设(T=kd)
[sumlimits_{k=1}^n ksumlimits_{d=1}^{lfloor frac{n}{k}
floor}mu(d)lfloor frac{n}{T}
floorlfloor frac{m}{T}
floor
]
把(d)换成(T)
[sumlimits_{T=1}^nsumlimits_{k|T} kmu(frac{T}{k})lfloor frac{n}{T}
floorlfloor frac{m}{T}
floor
]
(k)也就是(id(k)),可以写成
[sumlimits_{T=1}^nsumlimits_{k|T} id(k)mu(frac{T}{k})lfloor frac{n}{T}
floorlfloor frac{m}{T}
floor
]
因为(k*frac{T}{k} = T),所以在枚举(T)时,(k)和(frac{T}{k})可以看成相同的元素,只是顺序相反
(egin{array} ecause id*mu=varphi \ herefore sumlimits_{k|T}id(k)mu(frac{T}{k}) = sumlimits_{k|T}varphi(k) end{array})
(ecause varphi)是积性函数
( herefore sumlimits_{k|T}varphi(k) = varphi(T))
即原式可以转化为
[sumlimits_{T=1}^nvarphi(T)lfloor frac{n}{T}
floorlfloor frac{m}{T}
floor
]
莫比乌斯反演的部分到此结束了,但注意,题目要求的是([1,n])中每任意两个不同的数的(gcd)之和,
而上述做法多加了(gcd(i,i)),且重复计算了(gcd(i,j))和(gcd(j,i))
所以我们要把多余的部分减去,最终答案即 ((Ans-sumlimits_{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;
}