51nod oj 1678 lyk与gcd 【容斥定理+打表】

20 篇文章 0 订阅
7 篇文章 0 订阅


传送门:1678


基准时间限制:2 秒 空间限制:131072 KB 分值: 80  难度:5级算法题
 收藏
 关注

这天,lyk又和gcd杠上了。
它拥有一个n个数的数列,它想实现两种操作。


1:将    改为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 不互素的数的和---

总和-求的和就为所要的解


代码:

#include<cstdio>
#include<cmath> 
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
vector <int > sta[200100];
int shu[220000];
int ou[100],ll;
int qu[200100],kkp;
LL pp[200100];
void init(int n)
{
	int su[200100],kp=0;
	bool fa[200100];
	memset(fa,true,sizeof(fa));
	for (int i=2;i<=n;i++)
	{
		if (fa[i])
		{
			su[kp++]=i;
			if (i<=sqrt(n))
			for (int j=i*i;j<=n;j+=i)
				fa[j]=false;
		}
	}
	for (int i=2;i<=n;i++)
	{
		int ll=0;
		int kk=i;
		for (int j=0;su[j]*su[j]<=kk;j++)
		{
			if (kk%su[j]==0)
				ou[ll++]=su[j];
			while (kk%su[j]==0)
				kk/=su[j];
		}
		if (kk>1)
			ou[ll++]=kk;
		kkp=0;
		qu[kkp++]=-1;
		for (int j=0;j<ll;j++)
		{
			kk=kkp;
			for (int k=0;k<kk;k++)
				qu[kkp++]=qu[k]*ou[j]*-1;
		}
		for (int j=1;j<kkp;j++)
			sta[i].push_back(qu[j]);
	}
}
int main()
{
	int n,k;
	/*freopen("In.txt","r",stdin);
	freopen("wo.txt","w",stdout);*/
	scanf("%d%d",&n,&k);
	init(n);
	LL s=0,ans;
	memset(pp,0,sizeof(pp));
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&shu[i]);
		for (int j=0;j<sta[i].size();j++)
		{
			if (sta[i][j]>0)
				pp[sta[i][j]]+=shu[i];
			else
				pp[-sta[i][j]]+=shu[i];
		}
		s+=shu[i];
	}
	int a,b,c;
	while (k--)
	{
		scanf("%d",&c);
		if (c==1)
		{
			scanf("%d%d",&a,&b);
			for (int j=0;j<sta[a].size();j++)
			{
				if (sta[a][j]>0)
					pp[sta[a][j]]-=shu[a];
				else
					pp[-sta[a][j]]-=shu[a];
			}
			s-=shu[a];
			shu[a]=b;
			for (int j=0;j<sta[a].size();j++)
			{
				if (sta[a][j]>0)
					pp[sta[a][j]]+=shu[a];
				else
					pp[-sta[a][j]]+=shu[a];
			}
			s+=shu[a];
		}
		else
		{
			scanf("%d",&a);
			if (a==1)
			{
				printf("%lld\n",s);
				continue;
			}
			ans=0;
			for (int i=0;i<sta[a].size();i++)
			{
				if (sta[a][i]<0)
					ans-=pp[-sta[a][i]];
				else
					ans+=pp[sta[a][i]];
			}	
			ans=s-ans;
			printf("%lld\n",ans);
		}
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值