题目链接 https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1678
【题目描述】
这天,lyk又和gcd杠上了。
它拥有一个
n
n
个数的数列,它想实现两种操作。
1:将 改为
b
b
2:给定一个数i,求所有 时的
aj
a
j
的总和
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
i
求出所有
gcd(i,j)!=1
g
c
d
(
i
,
j
)
!
=
1
的数,然后用总和减去这些数对应的
a[j]
a
[
j
]
的值的和,难点在于用一个数组
c[x]
c
[
x
]
来表示所有
x
x
的倍数k对应的 的和,也就是
查询的时候用容斥原理,某个数 j j 和输入的不互素说明 gcd(id,j) g c d ( i d , j ) 一定是一个 id i d 的因子,对 id i d 做唯一分解,找出所有素因子,然后枚举素因子的所有组合情况,如果是奇数个素因子相乘所得的倍数那么答案就加上这一部分,否则减去
#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
int n,q;
int a[maxn],sum;
int c[maxn];
int fac[maxn],num;
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
sum+=a[i];
}
for(int i=1;i<=n;++i){
for(int j=1;i*j<=n;++j){
c[i]+=a[i*j];
}
}
while(q--){
int tp;
scanf("%d",&tp);
if(tp==1){
int id,val;
scanf("%d%d",&id,&val);
c[id]+=val-a[id];
int m=sqrt(id)+0.5;
for(int i=2;i<=m;++i){
if(id%i==0){
c[i]+=val-a[id];
if(id/i!=i) c[id/i]+=val-a[id];
}
}
sum+=val-a[id];
a[id]=val;
}
else{
int id;
scanf("%d",&id);
int m=sqrt(id)+0.5;
num=0;
for(int i=2;i<=m;++i){
if(id%i==0){
fac[num++]=i;
while(id%i==0) id/=i;
if(id==1) break;
}
}
if(id>1){ fac[num++]=id; }
int ans=0;
for(int s=1;s<(1<<num);++s){
int cnt=0;
int tmp=1;
for(int j=0;j<num;++j){
if(s>>j&1){
tmp*=fac[j];
++cnt;
}
}
if(cnt&1) ans+=c[tmp];
else ans-=c[tmp];
}
printf("%d\n",sum-ans);
}
}
return 0;
}