51nod 1687 lyk与gcd 状态压缩+容斥原理+质因数分解+

1678 lyk与gcd
基准时间限制: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

题解:首先发现直接通过gcd(i ,j)很难求解,只有打暴力。对于给定的数i,我求不与它互素的j的值的和,然后用总的和num减去就得到了答案。对于与i不互素的j,将i,j分解质因数后必定存在相同的质因数,所以可以定义一个数组val【k】表示下标数字含有因数k的值的总和,那么很容易表示出答案ans=num-(val[p1]+val[p2]+val[p3]……val[px]-val[p1*p2]-val[p1*p3]……)(容斥原理奇加偶减),比如:假设i = 60   素因数分解 并且只在意其 素因子 不在意个数 -> 2 3 5. (因为在计算2的时候 其实包含了4这样的情况。画图就能知道。) 那么j和i不互素的情况就是 j 含有 因子 2 或3 或5 。记 j含有因子2的数值和为S(2).  含有因子 2和3 的时候的数值和 为S(2,3) ,以此类推。可以知道。S(2)+S(3)+S(5) - S(2,3) - S(3,5) - S(2,5)  + S(2,3,5) 就是ans 。用状态压缩来表示容斥原理是本题最神的地方,值得学习。
总结:对于题目给定条件不好求解的时候那就从反面去求,用总的-不合法的=满足条件的。对于n个数有2^n-1种搭配方案,就是本题的重要性质,所以还是以前说的,一定要善于找到题目中的各种性质,其往往是解题的关键;

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define N 300000
using namespace std;
int n,Q,num;
int date[N],val[N],re[N];
int main()
{
//	freopen("hh.out","r",stdin);
	scanf("%d%d",&n,&Q);
	for(int i=1;i<=n;i++) scanf("%d",&date[i]),num+=date[i];
	for(int i=1;i<=n;i++)
	    for(int j=1;i*j<=n;j++)
	        val[i]+=date[i*j];
	int opt,x,y;
	while(Q--)
	{
		scanf("%d",&opt);
		if(opt==1)
		{
			scanf("%d%d",&x,&y);
			int tmp=date[x]-y;
			int re=sqrt(x);
			for(int i=1;i<=re;i++)
			{
				if(!(x%i))
				{
				   if(i*i==x) val[i]-=tmp;
				   else val[i]-=tmp,val[x/i]-=tmp;				
				}
			}
			date[x]=y;num-=tmp;
			continue;
		}
		scanf("%d",&x);
		int t=x,cnt=0;
		for(int j=2;j*j<=t;j++)
		{
			if(!(t%j)) re[++cnt]=j;
			while(!(t%j)) t/=j;			
		}
		if(t!=1) re[++cnt]=t;
		long long ans=0;
		for(int i=1;i<(1<<cnt);i++)
		{
			int tle=1,mle=0;
			for(int j=1;j<=cnt;j++)
			{
				if(i&(1<<(j-1)))
				{
					tle*=re[j];mle++;
				}
			}
			if(mle&1) ans+=(long long)val[tle];
			else ans-=(long long)val[tle];
		}
		printf("%lld\n",num-ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值