51Nod 1678 莫比乌斯反演

题目链接


题意:
给定 n n n个数和 q q q个询问,询问有两种:
1. 1. 1.给定 i i i v a l val val,将 a [ i ] a[i] a[i]的值更新为 v a l val val
2. 2. 2.给定 i i i,求 ∑ j = 1 n a [ j ] ( g c d ( i , j ) = = 1 ) \sum_{j=1}^n a[j](gcd(i,j) == 1) j=1na[j](gcd(i,j)==1)


思路:

考虑询问操作的实现:
由艾弗森约定,得:
A n s = ∑ j = 1 n a [ j ] ( g c d ( i , j ) = = 1 ) = ∑ j = 1 n a [ j ] ∑ d ∣ g c d ( i , j ) μ [ d ] Ans = \sum_{j=1}^n a[j](gcd(i,j) == 1) = \sum_{j=1}^n a[j] \sum_{d|gcd(i,j)} \mu[d] Ans=j=1na[j](gcd(i,j)==1)=j=1na[j]dgcd(i,j)μ[d]

转换枚举变量为 d d d,则
A n s = ∑ d = 1 n μ [ d ] ∗ ∑ j = 1 ⌊ n d ⌋ a [ j ∗ d ] Ans = \sum_{d=1}^n \mu[d] * \sum_{j=1}^{\lfloor\frac{n}{d}\rfloor}a[j*d] Ans=d=1nμ[d]j=1dna[jd] ( d d d i i i的约数)


s u m [ x ] = ∑ i = 1 ⌊ n x ⌋ a [ i ∗ x ] sum[x] = \sum_{i=1}^{\lfloor\frac{n}{x}\rfloor} a[i*x] sum[x]=i=1xna[ix]

则:
A n s = ∑ d = 1 n μ [ d ] ∗ s u m [ d ] Ans = \sum_{d=1}^n \mu[d] *sum[d] Ans=d=1nμ[d]sum[d]
( d d d i i i的约数)

故询问的复杂度为: O ( n ) O(\sqrt n) O(n )

而对于更新操作,只需要更新 s u m sum sum中下标为 i i i的约数的元素值,复杂度为: O ( n ) O(\sqrt n) O(n )

另外需要预处理 s u m sum sum μ \mu μ,复杂度为 O ( n ) O(n) O(n)

总复杂度: O ( n + q n ) O(n + q\sqrt n) O(n+qn )


代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
typedef long long ll;
#define lson rt<<1
#define rson rt<<1|1

const int A = 1e5 + 10;
int a[A],sum[A],pri[A],mu[A],tot,n,q;
bool vis[A];

void init(){
    for(int i=0 ;i<=n ;i++) sum[i] = 0;
    for(int i=1 ;i<=n ;i++){
        for(int j=i ;j<=n ;j+=i){
            sum[i] += a[j];
        }
    }
    tot = 0,mu[1] = 1;
    for(int i=2 ;i<A ;i++){
        if(!vis[i]) pri[++tot] = i,mu[i] = -1;
        for(int j=1 ;j<=tot && i*pri[j] < A ;j++){
            vis[i*pri[j]] = 1;
            if(i%pri[j] == 0){
                mu[i*pri[j]] = 0;
                break;
            }
            mu[i*pri[j]] = -mu[i];
        }
    }
}

void update(int x,int val){
    for(int i=1 ;i*i<=x ;i++){
        if(x%i == 0){
            sum[i] = sum[i] - a[x] + val;
            if(i*i != x){
                sum[x/i] = sum[x/i] - a[x] + val;
            }
        }
    }
    a[x] = val;
}

ll query(int x){
    ll ans = 0;
    for(int i=1 ;i*i<=x ;i++){
        if(x%i == 0){
            ans += mu[i]*sum[i];
            if(i*i != x){
                ans += mu[x/i]*sum[x/i];
            }
        }
    }
    return ans;
}

int main(){
    scanf("%d%d",&n,&q);
    for(int i=1 ;i<=n ;i++) scanf("%d",&a[i]);
    init();

    while(q--){
        int type;scanf("%d",&type);
        if(type == 1){
            int id,x;
            scanf("%d%d",&id,&x);
            update(id,x);
        }
        else{
            int id;
            scanf("%d",&id);
            printf("%lld\n",query(id));
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值