[CodeForces 547C] Mike and Foam(莫比乌斯反演) | 错题本

文章目录

题目

[CodeForces 547C] Mike and Foam

分析

c i c_i ci 表示 i i i 的个数,要求 i < j i < j i<j 比较麻烦,考虑求 t = ∑ i = 1 n ∑ j = 1 n [ gcd ⁡ ( a i , a j ) = 1 ] t = \sum_{i = 1}^n\sum_{j = 1}^{n}[\gcd(a_i, a_j) = 1] t=i=1nj=1n[gcd(ai,aj)=1] 则答案为 t − c 1 2 \frac{t - c_1}{2} 2tc1。设 m m m 是最大值: t = ∑ i = 1 n ∑ j = 1 n [ gcd ⁡ ( a i , a j ) = 1 ] = ∑ d = 1 m μ ( d ) ( ∑ i = 1 ⌊ m d ⌋ c i ⋅ d ) 2 \begin{aligned} t = &\sum_{i = 1}^n\sum_{j = 1}^{n}[\gcd(a_i, a_j) = 1] \\ =& \sum_{d = 1}^m\mu(d) \left(\sum_{i = 1}^{\left\lfloor \frac{m}{d} \right\rfloor} {c_{i \cdot d}}\right)^2\end{aligned} t==i=1nj=1n[gcd(ai,aj)=1]d=1mμ(d)i=1dmcid2 f ( x ) = ∑ i = 1 ⌊ m x ⌋ c i ⋅ x f(x) = \sum\limits_{i = 1}^{\left\lfloor \frac{m}{x} \right\rfloor} {c_{i \cdot x}} f(x)=i=1xmcix,发现修改一个数涉及到要修改的 f f f 值是所有它的因数,枚举因数的时间复杂度是 O ( m ) O(\sqrt{m}) O(m ),因此暴力修改即可。

最开始处理 f f f 的复杂度是 O ( ∑ i = 1 m m i ) = O ( m ln ⁡ m ) O(\sum_{i = 1}^m\frac{m}{i}) = O(m \ln m) O(i=1mim)=O(mlnm),不会 T。

代码

#include <bits/stdc++.h>

typedef long long LL;

const int MAXN = 500000;

int N, M, Q;
int A[MAXN + 5];
LL Cnt[MAXN + 5], Num[MAXN + 5];

int Mu[MAXN + 5];
bool Vis[MAXN + 5];
std::vector<int> Primes;

void Init(int n) {
	Mu[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (!Vis[i])
			Mu[i] = -1, Primes.push_back(i);
		for (int j = 0; j < (int)Primes.size() && i * Primes[j] < n; j++) {
			Vis[i * Primes[j]] = true;
			if (i % Primes[j] == 0) {
				Mu[i * Primes[j]] = 0;
				break;
			}
			Mu[i * Primes[j]] = -Mu[i];
		}
	}
}

int main() {
	Init(MAXN);
	memset(Vis, 0, sizeof Vis);
	scanf("%d%d", &N, &Q);
	for (int i = 1; i <= N; i++)
		scanf("%d", &A[i]), M = std::max(M, A[i]);
	LL Ans = 0;
	while (Q--) {
		int pos; scanf("%d", &pos);
		int dlt = Vis[pos] ? -1 : 1;
		for (int i = 1; i * i <= A[pos]; i++) {
			if (A[pos] % i) continue;
			int j = A[pos] / i;
			if (j <= M / i) {
				Ans -= Num[i] * Num[i] * Mu[i];
				Num[i] += dlt;
				Ans += Num[i] * Num[i] * Mu[i];
			}
			if (i != j && i <= M / j) {
				Ans -= Num[j] * Num[j] * Mu[j];
				Num[j] += dlt;
				Ans += Num[j] * Num[j] * Mu[j];
			}
		}
		Cnt[A[pos]] += dlt;
		Vis[pos] = !Vis[pos];
		printf("%lld\n", (Ans - Cnt[1]) / 2);
	}
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值