传送门
题目大意:求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对。多组数据。
T = 10000
N, M <= 10000000
最近沉迷刷数论水题,弱得瑟瑟发抖~
枚举质数 p ,套路反演一波
枚举 pi
记 F(T)=∑p|Tμ(Tp) ,注意前提是 p 是质数。
考虑如何求 F(T) 的前缀和。好像不能筛了,但是 [1,n] 的质数好像只有 nln(n) 个。我们直接枚举质数和其倍数计算 F(T) 然后求前缀和就好了。
由于调和级数的性质, ∑ni=1⌊ni⌋≈nln(n) ,对于每个 i 均摊 ln(n) 。所以预处理 F(T) 前缀和的时间就是 O(nln(n))∗O(ln(n))=O(n) 。
询问时分一下块,可以做到 O(Tn√) 。
#include
#define maxn 10000010
#define temp (i * prime[j])
using namespace std;
typedef long long LL;
int T, n, m, cnt;
int prime[maxn];
bool Vis[maxn];
LL miu[maxn], F[maxn];
void Da(){
miu[1] = 1LL;
for(int i = 2; i < maxn; i++){
if(!Vis[i]){
prime[++cnt] = i;
miu[i] = -1LL;
}
for(int j = 1; j <= cnt && temp < maxn; j++){
Vis[temp] = true;
if(i % prime[j] == 0){
miu[temp] = 0LL;
break;
}
else miu[temp] = -miu[i];
}
}
for(int j = 1; j <= cnt; j++)
for(int i = 1; temp < maxn; i++)
F[temp] += miu[i];
for(int i = 2; i < maxn; i++) F[i] += F[i-1];
}
LL Solve(){
if(n > m) swap(n, m);
LL ans = 0LL;
int last;
for(int i = 1; i <= n; i = last+1){
last = min(n/(n/i), m/(m/i));
ans += 1LL * (n/i) * (m/i) * (F[last] - F[i-1]);
}
return ans;
}
int main(){
scanf("%d", &T);
Da();
while(T --){
scanf("%d%d", &n, &m);
printf("%lld\n", Solve());
}
return 0;
}