gcd 动态维护集合(Mobius)

10.3

gcd

思路:
用 f(i) 表示 gcd 为 i的数对个数, g(i) 表示 gcd 为 i的倍数的数对个数。那么 f(i)=Σμ(d)g(d) ,我们只需要维护 g(1)~g(max(xi)))。记 s(i) 表示 i的倍数个数,那么 g(i)=s(i)*(s(i)-1)/2,我们只需要在加入删除一个数时枚举它的因数修改s即可。
时间复杂度 O(msqrt(max(xi))

%Doggu
dfs构造因数,
注释中有推导

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define LL long long
#define N 200010
#define lim 500010
using namespace std;

int cnt[lim];
bool in[N], vis[lim];
int stack[7], top;
LL ans;
int prime[lim/10], ptot, minp[lim], mu[lim];
vector<int> PR[N];

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

void init() {
    mu[1] = 1;
    for(int i=2; i<=lim-10; i++) {
        if(!vis[i]) prime[++ptot] = i, minp[i] = i, mu[i] = -1;
        for(int j=1; j<=ptot; j++) {
            if((LL)i*prime[j] > lim) break;
            vis[i * prime[j]] = 1;
            minp[i * prime[j]] = prime[j];//预处理出最小的质因数 
            mu[i * prime[j]] = -mu[i];
            if(i % prime[j] == 0) { mu[i * prime[j]] = 0; break;}
        }
    }
}

void modify(int x) {
    vector<int>::iterator end = PR[x].end();
    if( !in[x] )//选择状态 
        for(vector<int>::iterator it = PR[x].begin(); it != end; it++) 
            ans += mu[*it] * cnt[*it], cnt[*it]++;//此数显然可以整除这个质因数 
    else 
        for(vector<int>::iterator it = PR[x].begin(); it != end; it++) 
            cnt[*it]--, ans -= mu[*it] * cnt[*it];
    in[x] ^= 1;
    printf("%I64d\n", ans);
}
/*
在集合S中加入一个数a,求其中与它互质的数的个数 
也就是求 singma([gcd(xi,a)==1]) 
= singma([gcd(xi,a)==1]) = singma(singma(mu(d))) (d|n)
= singma(mu(d)*(S中d的倍数的个数)) (d|n)
*/
inline void DFS(int i, int deep, int num) {
    if(deep == top + 1) { PR[i].push_back(num); return ;}
    DFS(i, deep+1, num*stack[deep]);
    DFS(i, deep+1, num);
}//构造出来了一个序列:包含了所有的d 

int main() {
    freopen ("gcd.in", "r", stdin);
    freopen ("gcd.out", "w", stdout);
    init();
    int n, q, x, tmp;
    n = read(); q = read();
    for(int i=1; i<=n; i++) {
        x = read();
        top = 0;
        while(x != 1) {
            stack[++top] = minp[x];
            tmp = minp[x];
            while(x % tmp == 0) x /= tmp;
        }
        DFS(i, 1, 1);
    }
    while( q-- ) {
        x = read();
        modify( x );
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值