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

题目

题目链接: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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值