【容斥原理】Codeforces#547 Div1 C.Mike and Foam

https://codeforces.com/contest/547/problem/C

题目描述

原题来自:Codeforces#547 Div1 C.Mike and Foam
翻译来源:牛客网

M i k e Mike Mike R i c o Rico Rico 酒吧的调酒师。在 R i c o Rico Rico 酒吧,他们将啤酒杯放在一个特殊的架子上。在 R i c o Rico Rico 酒吧,有 n n n 种啤酒编号从 1 1 1 n n n 。第 i i i 瓶啤酒上面有 a i a_{i} ai 毫升的泡沫。

M a x i m Maxim Maxim M i k e Mike Mike 的老板。今天他让 M i k e Mike Mike 回答 q q q 个查询。最初架子是空的。在每个操作中, M a x i m Maxim Maxim 给他一个编号XX。如果编号为 X X X 的啤酒已经在架子上,那么 M i k e Mike Mike 应该从架子上取下它,否则他应该把它放在架子上。

每次询问后, M i k e Mike Mike 应该告诉他架子的分数。他们认为货架的分数是满足 i < j i<j i<j 并且 g c d ( a i , a j ) = 1 gcd(a_{i},a_{j})=1 gcd(ai,aj)=1 的数对 ( i , j ) (i,j) (i,j) 的个数。

M i k e Mike Mike 现在很累。所以他请你帮他处理这些操作。

Solution

(本题容斥的应用真的很妙)
反面考虑, a n s = ans = ans= 当前柜台上的个数 − a [ x ] - a[x] a[x] 与柜台上的数不互质的个数
我们用一个 s u m sum sum 数组来维护当前柜台上的数的因数出现的次数,也就是维护 i i i 是当前柜台上多少个数的因子,这个值存在 s u m [ i ] sum[i] sum[i] 中。
a [ x ] a[x] a[x] 与柜台上的数不互质的个数 = a [ x ] a[x] a[x] 是柜台上的一个质因数的倍数的个数 - a [ x ] a[x] a[x] 是柜台上的两个质因数的倍数的个数 + + + . . . ... ... + + + ( − 1 ) i ∗ a [ x ] (-1)^i*a[x] (1)ia[x] 是柜台上的 i i i 个质因数的倍数的个数

Code

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 500010;
#define int long long
int primes[N], vis[N], cnt;
int a[N];
bool used[N];
int sum[N];

signed main(){
    for (int i=2; i<=500000; i++){
        if(!vis[i]) vis[i]=i, primes[++cnt] = i;
        for (int j=1; j<=cnt&&i*primes[j]<=500000; j++){
            vis[i*primes[j]] = primes[j];
            if(i%primes[j]==0) break;
        }
    }
    int n, q;
    cin >> n >> q;
    for (int i=1; i<=n; i++) cin >> a[i];
    int ans = 0;
    while (q--){
        int x;
        cin >> x;
        if(!used[x]){
            vector<int> v;
            int cur = a[x];
            while (cur > 1)
                v.push_back(vis[cur]), cur /= vis[cur];
            sort(v.begin(),v.end());
            v.erase(unique(v.begin(),v.end()),v.end());
            int m = (int)v.size();
            for (int i=0; i<1<<m; i++){
                int tmp = 1;
                int t = 1;
                for (int j=0; j<m; j++) if(i>>j&1){
                    t *= v[j];
                    tmp = -tmp;
                }
                ans += tmp * sum[t];
                sum[t] ++;
            }
            cout << ans << '\n';
        }else{
            vector<int> v;
            int cur = a[x];
            while (cur > 1)
                v.push_back(vis[cur]), cur /= vis[cur];
            sort(v.begin(),v.end());
            v.erase(unique(v.begin(),v.end()),v.end());
            int m = (int)v.size();
            for (int i=0; i<1<<m; i++){
                int tmp = 1;
                int t = 1;
                for (int j=0; j<m; j++) if(i>>j&1){
                    t *= v[j];
                    tmp = -tmp;
                }
                sum[t] --;
                ans -= tmp * sum[t];
            }
            cout << ans << '\n';
        }
        used[x] = !used[x];
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值