题目
给定,求
且
为质数的
有多少对
数据范围:组询问,
题解
这是一道练习莫比乌斯反演的模板题,下面先给出莫比乌斯反演的做法,最后再一种欧拉函数计算的思路
对于这种二元组的题,我们设
分别为
为
倍数与正好为
的个数(范围内)
即:,两者关系有
,对其进行莫比乌斯反演
,则原题的
继续化解得,这里有个
如果是单次询问则可以直接处理
,
(最外层)前缀和再利用整除分块(对内层求和上标分类)进行计算,时间复杂度大概单次
,不过由于询问过多还需要进一步化解(如有需求可以在单次询问情况写一写)
if(N > M) swap(N, M);
int l, r;
for(l = 2; l <= N; l = r + 1){
r = min(N / (N / l), M / (M / l));
ans += solve(N / l, M / l) * (sum[r] - sum[l - 1]);
//solve()对最内层求和,sum[]维护最外层前缀和
}
再思考一下,莫比乌斯反演求和的关键是整除分块,那么我们考虑将最内层的移至外层进行进一步化解
,通过预处理
前缀和(大概
)后针对每次询问即可以在
内处理得到结果
PS:这里注意不要乱用(莫名
)
一开始我写的
LL N,M;
LL solve(LL, LL){
LL l,r;
...
}
调试了近半小时才,改成才发现竟然快了不止一倍 (╯#-_-)╯╧═╧
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
using LL = long long;
const int MAXN = 1e7 + 5;
int T, N, M;
bool noprime[MAXN];
int prime[MAXN], cnt_p, mu[MAXN];
void Euler_sieve(int top);
int g[MAXN], pre_g[MAXN];
LL solve(int, int);
int main(){
Euler_sieve(1e7);
int i, j;
for(i = 1; i <= cnt_p; i++)
for(j = prime[i]; j <= 1e7; j += prime[i])
g[j] += mu[j / prime[i]];
for(i = 2; i <= 1e7; i++) pre_g[i] = pre_g[i - 1] + g[i];
cin >> T;
while(T--){
cin >> N >> M;
LL ans = 0;
ans = solve(N, M);
cout << ans << endl;
}
return 0;
}
LL solve(int N, int M){
if(N > M) swap(N, M);
int l, r; LL res = 0;
for(l = 2; l <= N; l = r + 1){
r = min(N / (N / l), M / (M / l));
res += (LL)(N / l) * (M / l) * (pre_g[r] - pre_g[l - 1]);
}
return res;
}
void Euler_sieve(int top){
int i, j;
mu[1] = 1;
for(i = 2; i <= top; i++){
if(!noprime[i])
prime[++cnt_p] = i, mu[i] = -1;
for(j = 1; j <= cnt_p && prime[j] * i <= top; j++){
noprime[prime[j] * i] = true;
if(i % prime[j] == 0)
break;
mu[i * prime[j]] = -mu[i];
}
}
}
最后这里再给出欧拉函数的思路(这里限制GCD)
主要思路可以看gcd求和中第二第三部分
这里介绍第三部分思路(主要原文另一种写的比较清楚)
,
,则每个素数
贡献为
即可以计算得出GCD
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
using LL = long long;
const int MAXN = 1e7 + 5;
LL N, ans;
bool noprime[MAXN];
int prime[MAXN], cnt_p, phi[MAXN];
void Euler_sieve(int top);
LL f[MAXN];
int main(){
cin >> N;
Euler_sieve(N);
int i, j;
for(i = 2; i <= N; i++)
if(!noprime[i])
for(j = 2; j * i <= N; j++)
f[i] += phi[j];
for(i = 2; i <= N; i++)
if(!noprime[i]){
ans += 2 * f[i];
ans++;
}
cout << ans << endl;
return 0;
}
void Euler_sieve(int top){
int i, j;
phi[1] = 1;
for(i = 2; i <= top; i++){
if(!noprime[i])
prime[++cnt_p] = i, phi[i] = i - 1;
for(j = 1; j <= cnt_p && prime[j] * i <= top; j++){
noprime[prime[j] * i] = true;
if(i % prime[j] == 0){
phi[i * prime[j]] = phi[i] * prime[j];
break;
}else{
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
}
}