bzoj2820 - YY的GCD (莫比乌斯函数,筛法,数论分块)
题目
给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对(多组输入)
题解
前置题目:
[sumlimits_p{sumlimits^n_{i=1}{sumlimits^m_{j=1}{[gcd(i,j)=p]}}}
]
[sumlimits_p{sumlimits^{frac{n}{p}}_{i=1}{sumlimits^{frac{m}{p}}_{j=1}{sumlimits_{d|gcd(i,j)}{mu(d)}}}}
]
[sumlimits_p{sumlimits_{d}{mu(d)sumlimits^{frac{n}{p}}_{i=1}{[d|i]sumlimits^{frac{m}{p}}_{j=1}{[d|j]}}}}
]
[sumlimits_p{sumlimits_{d}{mu(d) lfloorfrac{n}{pd}
floorlfloorfrac{m}{pd}
floor}}
]
令(T=pd)
[sumlimits^{min(n, m)}_{T=1}{sumlimits_{p|T}{mu(frac{T}{p}) lfloorfrac{n}{T}
floorlfloorfrac{m}{T}
floor}}
]
令(f(T)=sumlimits_{p|T}{mu(frac{T}{p})}),它可以用埃式筛筛出来。然后求得(f(T)),接下来就是简单的数论分块了。细节详见代码。
#include <bits/stdc++.h>
#define endl '
'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f
const int N = 1e7 + 10;
const double eps = 1e-5;
ll f[N];
int mu[N];
int cnt;
int prime[N];
bool isnp[N];
ll pre[N];
void init() {
mu[1] = 1;
f[1] = 0;
for(int i = 2; i < N; i++) {
if(!isnp[i]) {
mu[i] = -1;
prime[cnt++] = i;
}
for(int j = 0; j < cnt; j++) {
int p = prime[j];
if(i * p >= N) break;
isnp[i * p] = 1;
if(i % p == 0) {
mu[i * p] = 0;
break;
}
mu[i * p] = -mu[i];
}
}
for(int i = 2; i < N; i++) { //埃式筛筛f(T)
if(!isnp[i]) {
f[i] = 1;
for(int j = 2 * i; j < N; j += i) {
f[j] += mu[j / i];
}
}
}
pre[0] = 0;
for(int i = 1; i < N; i++) pre[i] += pre[i - 1] + f[i]; //前缀和
}
int main() {
IOS;
init();
int t;
cin >> t;
while(t--) {
int n, m;
cin >> n >> m;
ll ans = 0;
int cur = 1;
while(cur <= min(n, m)) { //数论分块
int p1 = n / (n / cur);
int p2 = m / (m / cur);
int nt = min(min(n, m), min(p1, p2));
ans += 1ll * (n / cur) * (m / cur) * 1ll * (pre[nt] - pre[cur - 1]);
cur = nt + 1;
}
cout << ans << endl;
}
}