luogu2257 YY的GCD(莫比烏斯反演)
阿新 • • 發佈:2018-12-02
題目描述:神犇YY虐完數論後給傻×kAc出了一題
給定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)為質數的(x, y)有多少對
kAc這種傻×必然不會了,於是向你來請教……
多組輸入
輸入格式:第一行一個整數T 表述資料組數
接下來T行,每行兩個正整數,表示N, M
輸出格式:T行,每行一個整數表示第i組資料的結果
輸入樣例:
2
10 10
100 100
輸出樣例:
30
2791
解析:莫比烏斯反演中比較基礎的題,也是我第一次做的莫比烏斯反演題。
設f(n)表示gcd為n的數對的對數,F(n)表示gcd為n或n的倍數的數對的對數。
那麼顯然 \(F(n) = \sum_{n|d}f(d)\)
反演之後得 \(f(n) = \sum_{n|d}μ(⌊\frac{d}{n}⌋)F(d)\)
而 F(n) = \(⌊\frac{N}{n}⌋⌊\frac{M}{n}⌋\)
所以可得 \(f(n) = \sum_{n|d}μ(⌊\frac{d}{n}⌋)⌊\frac{N}{n}⌋⌊\frac{M}{n}⌋\)
所求的 ans = \(\sum_{n∈prim}f(n)\)
ans = \(\sum_{n∈prim}\sum_{n|d}μ(⌊\frac{d}{n}⌋)⌊\frac{N}{n}⌋⌊\frac{M}{n}⌋\)
換一個列舉項,列舉 \(⌊\frac{d}{n}⌋\)
將dn換成t,則 ans = \(\sum_{T=1}^{min(N,M)}\sum_{t|T,t∈prim}μ(⌊\frac{T}{t}⌋)⌊\frac{N}{T}⌋⌊\frac{M}{T}⌋\)
即 ans = \(\sum_{T=1}^{min(N,M)}⌊\frac{N}{T}⌋⌊\frac{M}{T}⌋\sum_{t|T,t∈prim}μ(⌊\frac{T}{t}⌋)\)
到了這裡,只要套上一個整除分塊就做好了
μ可以直接篩出來,維護一個字首和即可
程式碼如下:
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 1e7 + 5;
int q, mu[maxn], mark[maxn], prim[maxn], tot, g[maxn], n, m;
ll sum[maxn], ans;
int read(void) {
char c; while (c = getchar(), c < '0' || c >'9'); int x = c - '0';
while (c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; return x;
}
void get_mu(void) { //篩mu函式
mu[1] = 1; mark[1] = 1;
for (int i = 2; i <= maxn - 5; ++ i) {
if (!mark[i]) mu[i] = -1, prim[++ tot] = i;
for (int j = 1; j <= tot; ++ j) {
if (prim[j] * i > maxn - 5) break;
mark[prim[j] * i] = 1;
if (i % prim[j] == 0) break;
else mu[i * prim[j]] = -mu[i];
}
}
for (int i = 1; i <= tot; ++ i) //統計mu函式的字首和
for (int j = 1; j * prim[i] <= maxn - 5; ++ j) g[j * prim[i]] += mu[j];
for (int i = 1; i <= maxn - 5; ++ i) sum[i] = sum[i - 1] + g[i];
}
int main() {
q = read();
get_mu();
while (q --) {
ans = 0;
n = read(); m = read();
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) * (sum[r] - sum[l - 1]);
}
printf("%lld\n", ans);
}
return 0;
}