一道莫比乌斯反演好题

前言

由于我昨天在旅游,没能及时更新博客,在这里想大家致歉。

题目描述

n n 个正整数 X 1 , X 2 X n X_1,X_2\dots X_n ,每个数字有一个状态,选中或者未选中,一开始所有的数都没有选中。

m m 个操作,每个操作给定一个编号 i i ,将 X i X_i 的选取状态取反。

每次操作后,计算选取的数中有多少个互质的无序数对。

数据范围

20%的数据, n 1000 n\le1000 m 1000 m\le1000

另外30%的数据, X i 100 X_i\le100

100%的数据, n 200000 n\le200000 m 200000 m\le200000 X i 500000 X_i\le500000 1 i n 1\le i\le n

题解

莫比乌斯反演。

f ( i ) f(i) gcd = i \gcd=i 的数对, g ( i ) g(i) gcd \gcd i i 倍数的数对, h ( i ) h(i) 为是 i i 倍数的个数,

g ( i ) = h ( i ) ( h ( i ) 1 ) 2 g(i)=\frac{h(i)(h(i)-1)}2 (显然只有两个数都是 i i 的倍数,它们的 gcd \gcd 才是 i i 的倍数)

而且 g ( i ) = i d f ( d ) g(i)=\sum_{i\mid d}f(d)

由第二类莫比乌斯反演得 f ( i ) = i d μ ( d i ) g ( d ) f(i)=\sum_{i\mid d}\mu(\frac di)g(d)​

所以 f ( 1 ) = i μ ( i ) g ( i ) f(1)=\sum_i\mu(i)g(i)

所以每次修改时用 X \sqrt X 的复杂度修改 X X 约数的 h h 值,然后修改相关的 g g 并修改 f ( 1 ) f(1) 即可。

#include<bits/stdc++.h>
using namespace std;
const int N=500005;
#define ll long long
int n,m,x[N],y[N],a[N],u[N],p[N],tot,v[N];
ll ans;
void doo(int o,int p){
    if(p)ans+=u[o]*(a[o]++);
    else ans-=u[o]*(--a[o]);
}
int main(){
    u[1]=1;
    for(int i=2;i<N;i++){
        if(!v[i])p[++tot]=i,u[i]=-1;
        for(int j=1;j<=tot&&i*p[j]<N;j++){
            v[i*p[j]]=1;
            if(i%p[j]==0)break;u[i*p[j]]=-u[i];
        }
    }
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&x[i]);
    while(m--){
        int o,opt=1;scanf("%d",&o);
        if(y[o])opt=0;y[o]^=1;
        int s=sqrt(x[o]);
        for(int i=1;i<=s;i++)if(x[o]%i==0){doo(i,opt);if(i!=x[o]/i)doo(x[o]/i,opt);}
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_27327327/article/details/83239333