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;
}