互 质 对 互质对 互质对
有n个数字,a[1],a[2],…,a[n]。有一个集合,刚开始集合为空。然后有一种操作每次向集合中加入一个数字或者删除一个数字。每次操作给出一个下标x(1 ≤ x ≤ n),如果a[x]已经在集合中,那么就删除a[x],否则就加入a[x]。
问每次操作之后集合中互质的数字有多少对。
注意,集合中可以有重复的数字,两个数字不同当且仅当他们的下标不同。
比如a[1]=a[2]=1。那么经过两次操作1,2之后,集合之后存在两个1,里面有一对互质
最 初 想 法 \color{blue}{最初想法} 最初想法
考虑每次 加 入 / 删 除 加入/删除 加入/删除 一个数字对答案的影响,
将数字 x x x 分解质因数, 发现可以通过把质因子记录在 桶 内快速得知数 x x x 与集合中哪些质因子重复, 但是不知道质因子具体属于哪些数 .
正 解 部 分 \color{red}{正解部分} 正解部分
分解质因子
→
\rightarrow
→ 分解因子
=
A
C
=AC
=AC , 一步之遥= =
现在的目标是:
x
x
x 与集合内的多少数字互质 .
设
x
=
p
1
∗
p
2
∗
.
.
.
∗
p
m
x = p_1*p_2*...*p_m
x=p1∗p2∗...∗pm, (指数懒得打了…),
分解因子后存到对应的桶 c n t [ ] cnt[] cnt[] 里,
n u m 互 质 = t o t − c n t [ p 1 ] − c n t [ p 2 ] − . . . + c n t [ p 1 ∗ p 2 ] + . . . − . . . + . . . num_{互质}=tot - cnt[p_1]-cnt[p_2]-...+cnt[p_1*p_2]+...-...+... num互质=tot−cnt[p1]−cnt[p2]−...+cnt[p1∗p2]+...−...+... .
容斥系数为 莫比乌斯函数, 没学过的可以看 这里 .
实 现 部 分 \color{red}{实现部分} 实现部分
像这种数组下标和值容易混淆的题目, 要注意区分 …
#include<cstdio>
#include<cstring>
typedef long long ll;
#define reg register
//{{{ define
const int maxn = 5e5 + 10;
int N;
int Q;
int p_cnt;
int A[maxn];
int p[maxn];
int mu[maxn];
bool Used[maxn];
ll Ans;
ll cnt[maxn];
//}}}
void sieve(){
mu[1] = 1;
for(reg int i = 2; i < maxn; i ++){
if(!Used[i]) p[++ p_cnt] = i, mu[i] = -1;
for(reg int j = 1, t; j <= p_cnt && (t = p[j]*i) < maxn; j ++){
Used[t] = 1;
if(i % p[j] == 0){ mu[t] = 0; break ; };
mu[t] = -mu[i];
}
}
} //
void Add(int x){
for(reg int d = 1; d*d <= x; d ++){
if(x % d) continue ;
Ans += mu[d] * (cnt[d] ++);
int d2 = x / d;
if(d != d2) Ans += mu[d2] * (cnt[d2] ++);
}
}
void Del(int x){
for(reg int d = 1; d*d <= x; d ++){
if(x % d) continue ;
Ans -= mu[d] * (-- cnt[d]);
int d2 = x / d;
if(d != d2) Ans -= mu[d2] * (-- cnt[d2]);
}
}
void Work(){
int x;
scanf("%d", &x);
!Used[x]?Add(A[x]):Del(A[x]);
Used[x] ^= 1;
printf("%lld\n", Ans);
}
int main(){
scanf("%d%d", &N, &Q);
for(reg int i = 1; i <= N; i ++) scanf("%d", &A[i]);
sieve(); memset(Used, 0, sizeof Used);
while(Q --) Work();
return 0;
}