基准时间限制:2 秒 空间限制:131072 KB 分值: 80
难度:5级算法题
这天,lyk又和gcd杠上了。
它拥有一个n个数的数列,它想实现两种操作。
1:将 ai 改为b。
2:给定一个数i,求所有 gcd(i,j)=1 时的 aj 的总和。
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
想到了容斥原理,但只写过最基础的 对一个数,容斥原理求与这个数互斥的个数,求互斥的数组还是第一次见(其实是容斥没怎么学会,逃)
首先开一个sum[i]用于记录 数组中索引为i的倍数的和
更新x位置时,只需要将所有包含x这个位置的所有索引更新
查询的时候,要找与i互质的所有位置,我们可以先分解出i的所有素因子,然后对素因子枚举子集,得到素因子组合的每一种情况,此时就可以用容斥原理(包含一个素因子-包含两个素因子+包含三个素因子...)求出所有与i不互素的位置的和,然后用总和减去即可
具体看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+200;
int n,q;
int sum[N],num[N],tot;
vector<int>v;
void init()
{
for(int i=1;i<=n;i++)
for(int j=1;i*j<=n;j++)
sum[i]+=num[i*j];
}
void update(int x,int y)
{
tot-=num[x];
for(int i=1;i*i<=x;i++)
{
if(x%i==0)
{
if(i*i!=x)
sum[x/i]+=y-num[x];
sum[i]+=y-num[x];
}
}
num[x]=y;
tot+=num[x];
}
void getprime(int x)
{
v.clear();
for(int i=2;i*i<=x;i++)
{
if(x%i==0)
{
v.push_back(i);
while(x%i==0)x/=i;
}
}
if(x>1)v.push_back(x);
}
int query(int x)
{
getprime(x);
int res=0,len=v.size();
for(int i=1;i<(1<<len);i++)
{
int cnt=0,t=1;
for(int j=0;j<len;j++)
{
if(i&(1<<j))
{
t*=v[j];
cnt++;
}
}
if(cnt&1)
res+=sum[t];
else
res-=sum[t];
}
return res;
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
tot+=num[i];
}
init();
int op,x,y;
while(q--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d%d",&x,&y);
update(x,y);
}
else
{
scanf("%d",&x);
printf("%d\n",tot-query(x));
}
}
return 0;
}