题面:
求 (T = 10000) 次
[sum_{i=1}^n sum_{j=1}^m [gcd(i,j) 为质数]
]
(n) 、 (m) 规模均为 (1e7)。
一个常见的套路是若能找到 (g) 使得 (f = g*1) ((f、g均为数论函数)), 则
[sum_{i=1}^n sum_{j=1}^m f(gcd(i,j))
]
[=sum_{i=1}^n sum_{j=1}^m sum_{d|gcd(i,j)} g(d)
]
[=sum_{d=1}^{min(n,m)} g(d) * lfloor frac{n}{d}
floor lfloor frac{m}{d}
floor
]
由于(f = g * 1) , 则 (g = f * mu)。
在这道题里, (f(n) = [n为质数]), 则
(g(n) = sum_{d|n} [d为质数]mu(frac{n}d) = sum_{p|n} mu(frac{n}p))
所以
[sum_{i=1}^n sum_{j=1}^m [gcd(i,j) 为质数]
]
[= sum_{i=1}^n sum_{j=1}^m sum_{d|gcd(i,j)} g(d)
]
[=sum_{d=1}^{min(n,m)} g(d) * lfloor frac{n}{d}
floor lfloor frac{m}{d}
floor
]
其中, (g(n) = sum_{p|n} mu(frac{n}p)), 可以预处理。
总复杂度就是 (O(n + Tsqrt n))。
Luogu数据不开O2 AC代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e7 + 15;
#define li long long
int m, prime[maxn], v[maxn], f[maxn];
li g[maxn];
void euler(li n) {
for(li i=2; i<=n; ++i) {
if(!v[i]) {
v[prime[++m] = i] = i;
v[i] = i;
f[i] = 1;
}
for(int j=1; j<=m; ++j) {
if(prime[j] > n/i || prime[j] > v[i]) break;
v[prime[j] * i] = prime[j];
}
}
for(int i=1; i<=n; ++i) g[i] = f[i];
for(int i=1; i<=m; ++i)
for(int j=n/prime[i]; j>0; --j)
g[prime[i] * j] -= g[j];
}
li sol(int n, int m) {
int len = min(n,m);
li res = 0ll;
for(register int i=1,j; i<=len; i=j+1) {
j = min(n/(n/i), m/(m/i));
j = min(j, len);
res += (g[j]-g[i-1]) * (n/i) * (m/i);
}
return res;
}
int main()
{
euler(10000000);
for(int i=2; i<=10000000; ++i) g[i] += g[i-1];
int t; int n,m; cin >> t; while(t--) {
scanf("%d%d", &n, &m);
cout << sol(n, m) << '
';
}
return 0;
}```