傳送門
莫比烏斯反演的一個比較複雜做法(
令 \(f(i)\) 表示序列中 \(\operatorname {gcd}\) 為 \(i\) 的倍數的數對的個數, \(g(i)\) 為序列中 \(\operatorname {gcd}\) 為 \(i\) 的數對的個數,即 \(f(n) = \sum_{n|d} g(d)\) 。
\[f(n) = \sum_{n|d} g(d)
\]
\[g(n) = \sum_{n|d} \mu(\frac{d}{n})f(d)
\]
題目要求 \(\operatorname {gcd}(a[i],a[j]) = 1\) 的對數,即為求 \(g(1)\) 的值。
\[g(1) = \sum_{n|d} \mu(d)f(d)
\]
\(f(i)\) 該怎麼求?設 \(h(i)\) 為序列中為 \(i\) 的倍數的個數。例如 \(a[] = {1, 2, 3, 4, 6}\), \(h(2) = 3\) 。不難發現, \(f(i) = \frac{h(i) * (h(i) - 1)}{2}\)。
然後就修改的時候改一下 \(h(i)\) 的值即可,有一些小細節看程式碼好了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 7;
const int M = 500000;
int a[N];
int h[N], f[N], p[N], tot, mu[N], t[N], flg[N], vis[N], prime[N], e[N];
int ans = 0;
int n, q;
void getMu(){
e[1] = 1, mu[1] = 1;
for(int i = 2; i <= M; i ++) {
if(!e[i]) p[++ tot] = i, mu[i] = -1;
for(int j = 1; j <= tot; j ++) {
if(p[j] * i > M) break;
mu[p[j] * i] = i % p[j] == 0 ? 0 : -mu[i];
e[p[j] * i] = 1;
if(i % p[j] == 0) break;
}
}
}
void add(int x, int v) {
h[x] += v;
ans += ((h[x]) * (h[x] - 1) / 2 - f[x]) * mu[x];
f[x] = h[x] * (h[x] - 1) / 2;
}
void modify(int x, int v) {
for(int i = 1; i * i <= x; i ++) {
if(x % i == 0) {
add(i, v);
if(i * i != x) add(x / i, v);
}
}
}
signed main() {
getMu();
cin >> n >> q;
for(int i = 1; i <= n; i ++) {
cin >> a[i];
}
for(int i = 1; i <= q; i ++) {
int p;
cin >> p;
if(vis[p]) modify(a[p], -1), vis[p] = 0;
else {
modify(a[p], 1);
vis[p] = 1;
}
cout << ans << endl;
}
}