题目

有 $n$ 个正整数 $a_n$。现在有一个下标集合 $S$,初始时为空,你需要依次实现 $q$ 次操作:

每次操作会给定一个整数 $x(1\leq x\leq n)$,如果当前 $x$ 不在下标集合 $S$ 中,则在 $S$ 中加入 $x$,否则删去 $x$。 每次操作过后,你都需要计算下面这个式子的值:

$$\sum_{i<j,i,j\in S}[\gcd(a_i,a_j)=1]$$

分析

见到这样的式子,用莫比乌斯反演:

$$\begin{aligned}\sum_{i<j,i,j\in S}[\gcd(a_i,a_j)=1]&=\sum_{i<j,i,j\in S}\sum_{k|\gcd(a_i,a_j)}\mu(k)\\ &=\sum_k\mu(k)\sum_{k|a_i}\sum_{k|a_j}1\end{aligned}$$

那么每次对 $x$ 进行一个操作,只用枚举 $x$ 的所有因数 $k$(相当于枚举最大公约数),看看有多少个 $k$ 的倍数。时间复杂度 $\mathcal{O}(q\sqrt{n})$

代码

const int N = 1e5 + 10, M = 5e5 + 10;

inline ll Read() {
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

int n, t;
int a[N];

int pri[M], cnt, mu[M];
bool vis[M];
void Init() {
	mu[1] = 1;
	for (int i = 2; i <= M - 10; i++) {
		if (!vis[i]) pri[++cnt] = i, mu[i] = -1;
		for (int j = 1; j <= cnt && i * pri[j] <= M - 10; j++) {
			vis[i * pri[j]] = 1;
			mu[i * pri[j]] = -mu[i];
			if (i % pri[j] == 0) {
				mu[i * pri[j]] = 0;
				break;
			}
		}
	}
}

int cunt[M];
bool been[N];
ll ans;

int main() {
	freopen("pair.in", "r", stdin);
	freopen("pair.out", "w", stdout);
	Init();
	n = Read(), t = Read();
	for (int i = 1; i <= n; i++) a[i] = Read();
	for (; t--; ) {
		int now = Read();
		ll sum = 0;
		for (int i = 1; i * i <= a[now]; i++) {
			if (a[now] % i) continue;
			sum += mu[i] * cunt[i];
			cunt[i] += been[now]? -1: 1;
			if (i * i != a[now]) {
				sum += mu[a[now] / i] * cunt[a[now] / i];
				cunt[a[now] / i] += been[now]? -1: 1;
			}
		}
		if (been[now]) ans -= sum - (a[now] == 1);
		else ans += sum;
		been[now] ^= 1;
		printf ("%lld\n", ans);
	}
	return 0;
}
EOF

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。

博客信息

作者
Jayun
时间
2022-10-17 15:08:31
博客类型