CF547C Mike and Foam

你说得太对辣發表於2024-05-14

傳送門

莫比烏斯反演的一個比較複雜做法(

\(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;
	}
}