遗传病

题目大意:动态维护一个序列的gcd为1的数目

这题容斥...

两个数互质等价于质因数的集合交集为空集,记 |S| 为当前已登记的人中质因子集合的子集有S的个数,其中S是一个质因数集合

特别的|{1}| = 等于当前登记人数

与30互质的数的个数:|{1}| - |{2}| - |{3}| - |{5}| + |{2, 3}| + |{2, 5}| + |{3, 5}| - |{2, 3, 5}|

并且通过手算我们可以知道,素质子前六个撑起来就大于5e5了

所以\(2^6 \times n\)还是可以接受的

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 1e5 + 66;

int n, q, tot, las;
int a[N], cnt[N * 5];
int rec[N * 5][10];
bool vis[N * 5];

inline void yhm_div(int pos)
{
int i, x = a[pos];
for (i = 2; i * i <= x; ++ i)
{
if (x % i == 0)
{
rec[a[pos]][++ rec[a[pos]][0]] = i;
while (x % i == 0) x /= i;
}
}
if (x > 1) rec[a[pos]][++ rec[a[pos]][0]] = x;
return;
}

inline int func(int x)
{
int i, j, res(0), MAXSTATE = (1 << rec[x][0]);
for (i = 1; i < MAXSTATE; ++ i)
{
int tmp = 1, num = 0;
for (j = 0; j < rec[x][0]; ++ j) if (i & (1 << j)) tmp *= rec[x][j + 1], ++ num;
res += (num & 1) ? cnt[tmp] : -cnt[tmp];
}
return res;
}

inline void updata(int x, int delta)
{
int i, j, MAXSTATE = (1 << rec[x][0]);
for (i = 1; i < MAXSTATE; ++ i)
{
int tmp = 1;
for (j = 0; j < rec[x][0]; ++ j) if (i & (1 << j)) tmp *= rec[x][j + 1];
cnt[tmp] += delta;
}
return;
}

signed main()
{
int i, x, add;
n = read(), q = read();
for (i = 1; i <= n; ++ i) a[i] = read();
for (i = 1; i <= n; ++ i) if (! rec[a[i]][0]) yhm_div(i);

while (q --)
{
x = read(), add = 0;
if (! vis[x])
{
vis[x] = 1, add = tot ++;
if (a[x] != 1) add -= func(a[x]);
else ++ cnt[1];
updata(a[x], 1);
put((las += add));
}
else
{
vis[x] = 0, add = -- tot;
updata(a[x], -1);
if (a[x] != 1) add -= func(a[x]);
else -- cnt[1];
put((las -= add));
}
}

return 0;
}

后记:

此题思想相当好,非常巧妙也非常自然,水到渠成的感觉

但是小细节也多,比如updata的时候必须从1开始否则就会出问题