【TYZ 10/18】 最大公约数问题

题目描述:

TL:1S  ML:256MB 
【Description】 
有N个整数,kAc会对它们做Q次修改。 
每次修改指的是对所有数加一个整数(可正可负) 
每修改一次后,他想知道当前所有数的最大公约数是多少。 
【Input】 
第一行两个整数N, Q 
接下来N行,每行一个整数,表示这N个数的初始值。 
接下来Q行,每行一个整数,表示这Q个操作。第i个数表示这一次操作是增加了多少。 
【Output】 
共Q行,表示进行完第i次操作后,所有数的最大公约数 
【Sample Input】 
3 2 
1 -5 7 
-1 

【Sample Output】 


 
【Hint】 
对于40%:N, Q <= 1000 
对于70%:N, Q <= 40000 
对于100%:N, Q <= 100000,所有数的绝对值始终小于等于10^16 
在这里,我们认为任意非负整数x跟0的最大公约数都是x 

40%的数据就是一个暴力,注意留坑的是不能除以0

可以看出这是个o(n)的题目

这道题目时雨加减有关的

有个欧几里得定理

gcd(a,b)=gcd(a,a-b)

这个证明非常好想

毕竟是整除的

那么现在想差值

无论减去多少,差值总是不变的

•gcd(a[1], a[2]) = gcd(a[1], a[2] – a[1]) •gcd(a[2], a[3]) = gcd(a[2], a[3] – a[2]) •gcd(a[1], a[2], a[3]) = gcd(a[1], a[2] –a[1], a[3] – a[2]) • •gcd(a[1], … , a[n]) = gcd(a[1], a[2] –a[1] … ,a[n] – a[n – 1]) 这样子每次只算a1的值就可以了!

你要的O(n)

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<algorithm>
#include<cstring> 
using namespace std;
typedef long long ll;
const int maxn=100005;
ll a[maxn];
ll n,q,t1,t2;
ll gcd(ll xx,ll yy)
{
     if(yy==0)
     return xx;
     else
     return gcd(yy,xx%yy);
}
ll cgcd(ll xxx,ll yyy)
{
	if(xxx==0)
	return yyy;
	if(yyy==0)
	return xxx;
	xxx=abs(xxx),yyy=abs(yyy);
	return gcd(xxx,yyy);
}
ll calc(ll x)
{
	int cur=1;
	while((a[cur]+x)==0) cur++;
	if(cur==n+1) return 0;
	else
	{
		ll res=cgcd(a[cur]+x,a[cur+1]+x);
		for(ll i=cur+2;i<=n;i++)
		{
			res=cgcd(res,a[i]+x);
		}
		return res;
	}
}
int main()
{
	freopen("gcd.in","r",stdin);
	freopen("gcd.out","w",stdout);
	cin>>n>>q;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	}
     if(n<=1000&&q<=1000)
     {
     	ll sum=0;
     	for(int i=1;i<=q;i++)
     	{
     		scanf("%lld",&t1);
     		sum+=t1;
     		ll nw=calc(sum);
     		printf("%lld\n",nw);
		 }
   	  return 0;
	 }
	 else
	 {
	 	ll ans=cgcd(a[2]-a[1],a[3]-a[2]);
	 	for(int i=4;i<=n;i++)
	 	ans=cgcd(ans,a[i]-a[i-1]);
	 	ll sum=0;
	 	for(int i=1;i<=q;i++)
	 	{
	 		scanf("%lld",&t1);
	 		sum+=t1;
	 		ll w=cgcd(ans,a[1]+sum);
	 		printf("%lld\n",w);
		 }
		 return 0;
	  } 
}
点赞