51nod1678 lyk与gcd(莫比乌斯函数 打表)

题目

这天,lyk又和gcd杠上了。
它拥有一个n个数的数列,它想实现两种操作。
1:将  aiai 改为b。
2:给定一个数i,求所有 gcd(i,j)=1gcd(i,j)=1 时的  ajaj  的总和。

 收起

输入

第一行两个数n,Q(1<=n,Q<=100000)。
接下来一行n个数表示ai(1<=ai<=10^4)。
接下来Q行,每行先读入一个数A(1<=A<=2)。
若A=1,表示第一种操作,紧接着两个数i和b。(1<=i<=n,1<=b<=10^4)。
若B=2,表示第二种操作,紧接着一个数i。(1<=i<=n)。

输出

对于每个询问输出一行表示答案。

输入样例

5 3
1 2 3 4 5
2 4
1 3 1
2 4

输出样例

9
7

解题思路:问题可描述为\sum_{j=1}^{n}[gcd(i,j) = 1]*a_{j},对公式进行化简。

                                              \sum_{j=1}^{n}[gcd(i,j) = 1]*a_{j} = \sum_{j=1}^{n}\sum_{d|gcd(i,j)}\mu(d)*a_{j}= \sum_{d|i} \mu(d)*\sum_{j=1}^{\lfloor \frac{n}{d} \rfloor}a_{j*d}

sum(d)=\sum_{j=1}^{\lfloor \frac{n}{d} \rfloor}a_{j*d},那么\sum_{d|i} \mu(d)*\sum_{j=1}^{\lfloor \frac{n}{d} \rfloor}a_{j*d}=\sum_{d|i} \mu(d)*sum(d),sum(d)可以直接打表得到,复杂度为nlogn。

(1)对于修改操作我们可以发现只有当d|i时a[i]才会对sum[d]产生影响,因此我们在\sqrt n时间内枚举i的因子,修改对应的sum[d]。

(2)对于查询操作,我们枚举i的因子,在\sqrt n的时间内即可求得\sum_{d|i} \mu(d)*sum(d)

 

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int mu[maxn],mark[maxn],sum[maxn],prime[maxn],a[maxn];

void get(){
	mu[1] = 1;
	for(int i = 2;i < maxn;++i){
		if(!mark[i])	prime[++prime[0]] = i,mu[i] = -1;
		for(int j = 1;j <= prime[0] && i * prime[j] < maxn;++j){
			mark[i * prime[j]] = 1;
			if(i % prime[j] == 0)	break;
			mu[i * prime[j]] = -mu[i];
		}
	}
}

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

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

int main(){
	get();
	int n,q,o,b,c;
	
	scanf("%d%d",&n,&q);
	for(int i = 1;i <= n;++i)	scanf("%d",&a[i]);
	for(int i = 1;i <= n;++i){
		sum[i] = 0;
		for(int j = i;j <= n;j += i)
			sum[i] += a[j];
	}
	
	for(int i = 1;i <= q;++i){
		scanf("%d",&o);
		if(o == 1){
			scanf("%d%d",&b,&c);
			update(b,c);			
		}else{
			scanf("%d",&b);
			printf("%d\n",query(b));
		}
	}
	
	return 0;
} 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值