Codeforces #305 div2 E. Mike and Foam 数论 容斥原理

时间:2022-12-18 23:27:44

题目

题目链接:http://codeforces.com/problemset/problem/548/E

题目来源:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=105944#problem/N

简要题意:有一个集合,给定询问,每个询问给个位置,该位置的数在集合里就删去否则就加入,每轮求集合中互质的数对个数。

题解

维护某位置的数是否在集合中,然后维护一个数组保存集合中某数倍数的个数。

此处只需要考虑质数幂次均为一次的情况,因为这样就足够算出需要的值了。

对于每次询问先去分解素因数,更新个数的时候,对于每个素因子去二进制枚举然后更新。

更新结果的时候就去统计与该数互质的数的个数,容斥做就行了。

代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <set>
#include <map>
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
// head
const int N = 2E5+5;
const int M = 5E5+5;

int a[N];
bool add[N];
int cnt[M];

vector<int> v;

void decomp(int x) {
for (int i = 2; i*i <= x; i++) {
if (x % i) continue;
while (x % i == 0) x /= i;
v.push_back(i);
}
if (x > 1) v.push_back(x);
}
void update(int x) {
int mx = 1 << v.size();
for (int i = 0; i < mx; i++) {
int cur = 1;
for (int j = 0; j < v.size(); j++) {
if (i & (1<<j)) cur *= v[j];
}
cnt[cur] += x;
}
}
LL query() {
LL ans = 0;
int mx = 1 << v.size();
for (int i = 0; i < mx; i++) {
int cur = 1;
for (int j = 0; j < v.size(); j++) {
if (i & (1<<j)) cur *= v[j];
}
ans += __builtin_popcount(i)&1 ? -cnt[cur] : cnt[cur];
}
return ans;
}
int main() {
int n, q, x;
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d", a+i);
}

LL ans = 0;
while (q--) {
scanf("%d", &x);
int cur = a[x];
v.clear();
decomp(cur);
if (add[x]) {
update(-1);
ans -= query();
} else {
ans += query();
update(1);
}
add[x] = !add[x];
printf("%I64d\n", ans);
}
return 0;
}