problem
给出一个\(n,m(n,m\le 10^7)\),求\(\sum\limits_{i=1}^n\sum\limits_{j=1}^mgcd(i,j)\in P\)
P表示全部素数的集合。\(T,(T\le 10000)\)组询问
solution
枚举因数
\[原式=\sum\limits_{d\in P} \sum\limits_{i=1}^{\frac{n}{d}}\sum\limits_{j=1}^{\frac{m}{d}}\epsilon(gcd(i,j)=1) \]
因为\(\epsilon=1*\mu\)
\[所以上式=\sum\limits_{d\in P} \sum\limits_{i=1}^{\frac{n}{d}}\sum\limits_{j=1}^{\frac{m}{d}}\sum\limits_{k|i,k|j}\mu(k)\\ =\sum\limits_{d\in P}\sum\limits_{k=1}^{\frac{min(n,m)}{d}}\mu(k)\lfloor\frac{n}{dk}\rfloor\lfloor\frac{m}{dk}\rfloor \]
令\(t=dk\)
原式=$$\sum\limits_{t=1}^{min(n,m)}\lfloor\frac{n}{t}\rfloor \lfloor\frac{m}{t}\rfloor\sum\limits_{d\in P,d|t}\mu(\frac{t}{d})$$
发现前面这一块数论分块就好了,后面就提前预处理出来,求个前缀和。
预处理复杂度\(O(nloglogn)\),单次询问复杂度\(O(\sqrt{n})\)
code
/*
* @Author: wxyww
* @Date: 2020-01-20 19:08:26
* @Last Modified time: 2020-01-20 19:18:39
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
const int N = 10000010;
ll read() {
ll x=0,f=1;char c=getchar();
while(c<'0'||c>'9') {
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
int mu[N],pri[N],tot,vis[N];
void pre() {
mu[1] = 1;
for(int i = 2;i < N;++i) {
if(!vis[i]) pri[++tot] = i,mu[i] = -1;
for(int j = 1;j <= tot && 1ll * i * pri[j] < N;++j) {
vis[i * pri[j]] = 1;
if(i % pri[j]) mu[i * pri[j]] = -mu[i];
else {
mu[i * pri[j]] = 0;break;
}
}
}
}
int f[N];
int main() {
pre();
for(int i = 1;i <= tot;++i) {
int js = 1;
for(int k = pri[i];k < N;k += pri[i],++js) {
f[k] += mu[js];
}
}
for(int i = 1;i < N;++i) f[i] += f[i - 1];
int T = read();
while(T--) {
int n = read(),m = read();
ll ans = 0;
for(int l = 1,r;l <= min(n,m);l = r + 1) {
r = min(n / (n / l),m / (m / l));
ans += 1ll * (n / l) * (m / l) * (f[r] - f[l - 1]);
}
printf("%lld\n",ans);
}
return 0;
}