51nod1439 互质对 [莫比乌斯函数, 容斥]

互 质 对 互质对

有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=p1p2...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=totcnt[p1]cnt[p2]...+cnt[p1p2]+......+... .

容斥系数为 莫比乌斯函数, 没学过的可以看 这里 .


实 现 部 分 \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;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值