CF 547C. Mike ans Foam

Mike and Foam

题意

有长度为 \(n\) 的序列 \(a\) ,最开始里面的元素都未激活。
\(q\) 次询问,每次将一个元素从激活变成未激活,或者从未激活变成激活。

每次询问后,输出激活的数字中互质的序偶数量。

分析

直接对激活序列求互质序偶数量比较复杂,可以使用容斥原理。

序列互质序偶个数 = 所有序偶数量 - 不互质的序偶数量。

比如我们现在激活了 \(x\) ,那么我们就可以求出多加了多少不互质的序偶数量。
对于 \(x\) ,枚举所有和 \(x\) 有公共质因子的数字的数量。

\(cnt(i)\) 表示以 \(i\) 为因子的数字的数量。

那么如果我们减去了 \(cnt_2, cnt_3\) ,就一定要加上 \(cnt_6\) ,二进制枚举。
根据容斥原理,如果减去了奇数个质因子表示数字的数量,那么就减去,否则加上。

总结就是,求不互质的数量,先算出有一个质因子相同的所有数量,再减去有两个质因子,再加上有三个质因子的数量......

Code

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int N  = 1000005;

int a[N]; // 每瓶酒的容量
int cnt[N]; // cnt(i) 表示酒架上以i为因子的数字的数量
bool st[N]; // st(i) 表示第i瓶酒是否在酒架上
vector<int> fac[N]; // fac(i) 表示第i个数量的所有质因子

int main ()
{
    int n, q; cin >> n >> q;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    for (int i = 1; i <= n; i ++ )
    {
        // 求出每瓶酒容量的所有质因子
        int t = a[i];
        for (int j = 2; j <= t / j; j ++ )
        {
            int s = 0;
            while(t % j == 0) ++ s, t /= j;
            if (s) fac[i].push_back(j);
        }
        if (t > 1) fac[i].push_back(t);
    }
    
    long long ans = 0; // 每次查询的结果
    long long tot = 0; // 放上去的所有数量
    for (int i = 1; i <= q; i ++ )
    {
        int x; cin >> x;
        if (st[x]) // 存在,要拿下来
        {
            st[x] = false;
            int siz = fac[x].size();
            // 首先先把这个数字拿下来
            for (int i = 1; i < 1 << siz; i ++ )
            {
                int num = 1;
                for (int j = 0; j < siz; j ++ )
                    if (i >> j & 1) num *= fac[x][j];
                -- cnt[num];
            }
            
            // 求出和它不是互质的数量,所有瓶子减去就是互质的数量
            // 二进制枚举所有不互质的数字
            for (int i = 1; i < 1 << siz; i ++ )
            {
                int sign = 0, num = 1;
                for (int j = 0; j < siz; j ++ )
                    if (i >> j & 1) // 这个因子存在
                        ++ sign, num *= fac[x][j];
                
                if (sign & 1) sign = 1; else sign = -1;
                ans -= cnt[num] * sign;
            }
            -- tot;
        }
        else // 不存在,放上去
        {
            st[x] = true;
            int siz = fac[x].size();
            // 找出和他互质的数量
            for (int i = 1; i < 1 << siz; i ++ )
            {
                int sign = 0, num = 1;
                for (int j = 0; j < siz; j ++ )
                    if (i >> j & 1) ++ sign, num *= fac[x][j];
                
                if (sign & 1) sign = 1; else sign = -1;
                ans += cnt[num] * sign;
            }
            
            // 把这个数字放上去
            for (int i = 1; i < 1 << siz; i ++ )
            {
                int num = 1;
                for (int j = 0; j < siz; j ++ )
                    if (i >> j & 1) num *= fac[x][j];
                ++ cnt[num];
            }
            ++ tot;
        }
        // 输出结果
        cout << tot * (tot-1) / 2 - ans << endl;
    }
    return 0;
}
posted @ 2021-11-20 14:34  Horb7  阅读(36)  评论(0编辑  收藏  举报