传送门
这天,lyk又和gcd杠上了。
它拥有一个n个数的数列,它想实现两种操作。
1:将
ai
改为
b
。
2:给定一个数i,求所有
Input
第一行两个数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)。
Output
对于每个询问输出一行表示答案。
Input示例
5 3
1 2 3 4 5
2 4
1 3 1
2 4
Output示例
9
7
解题思路:
这个题目,我们可以从反面考虑,求
i和j
不互素的和,然后用总和减去就得到需要的结果了。
首先,在主函数外面进行素数筛,将所有的素数筛出来,为查询的时候做准备(其实是为了容斥做准备)
然后用一个
sum
值记录
1−n
所有数字的总和
在然后,我们预处理一下
1−i
每个数的倍数的和,比如说
tmp[i]
表示
1−n
中所有是
i
的倍数的数字的和。(为了下面进行容斥的时候方便)
然后如果进行的是第一个操作,即修改值的操作,那么我们将这个下标下的所有因子(注意是因子,不是素因子)的
如果是第二个操作,即查询操作,那么我们直接对查询的
i
<script type="math/tex" id="MathJax-Element-75">i</script> 进行素因子分解,得到素因子之后,然后进行容斥求解即可,容斥过程在代码中有详细过程,就不做解释了。
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;
typedef long long LL;
const int MAXN = 1e5+5;
int p[MAXN];
int prime[MAXN];
int cnt;
void isPrime(){
memset(prime, 0, sizeof(prime));
prime[1] = 1;
cnt = 0;
for(int i=2; i<MAXN; i++){
if(!prime[i]){
p[cnt++] = i;
for(int j=i+i; j<MAXN; j+=i)
prime[j] = 1;
}
}
}
int a[MAXN], tmp[MAXN];
int fac[MAXN/100];
int main()
{
isPrime();
int n, q;
while(~scanf("%d%d",&n,&q))
{
int sum = 0;
///memset(tmp, 0, sizeof(tmp));
for(int i=1; i<=n; i++) scanf("%d",&a[i]), sum += a[i];
for(int i=2; i<=n; i++) for(int j=1; i*j<=n; j++) tmp[i] += a[i*j];
while(q--){
int op;
scanf("%d",&op);
if(op == 1){
int ind, val;
scanf("%d%d",&ind, &val);
sum -= a[ind];
tmp[ind] -= a[ind];
tmp[ind] += val;
for(int i=2; i*i<=ind; i++){
if(i*i == ind){
tmp[i] -= a[ind];
tmp[i] += val;
continue;
}
if(ind%i == 0){
tmp[i] -= a[ind];
tmp[i] += val;
tmp[ind/i] -= a[ind];
tmp[ind/i] += val;
}
}
a[ind] = val;
sum += val;
}
else{
int ind;
scanf("%d",&ind);
int num = 0, tp = ind;
for(int i=0; p[i]*p[i]<=tp&&i<cnt; i++)
if(tp % p[i] == 0){
fac[num++] = p[i];
while(tp % p[i] == 0) tp /= p[i];
}
if(tp > 1) fac[num++] = tp;
int status = (1<<num);
int ans = 0;
for(int i=1; i<status; i++){
int tt = 1, cntt = 0;
for(int j=0; j<num; j++){
if(i&(1<<j)){
tt *= fac[j];
cntt++;
}
}
if(cntt & 1) ans += tmp[tt];
else ans -= tmp[tt];
}
printf("%d\n",sum - ans);
}
}
}
return 0;
}
/**
6 6
1 2 3 4 5 6
2 6
1 6 1
2 6
*/