[SDOI2009]SuperGCD

题目:BZOJ1876、洛谷P2152、codevs2305。

题目大意:给你两个数a,b($1leq a,b leq 10^{10000}$),让你求他们的最大公因数。

解题思路:一道高精度问题。首先我们想到的是压位,8位9位都可以。

但是如果用普通的欧几里得算法,肯定会TLE。

我们可以使用Stein算法。具体思路如下。

1.如果a和b都是偶数,那么gcd(a,b)=gcd(a/2,b/2)*2;

2.如果a是偶数,b是奇数,那么gcd(a,b)=gcd(a/2,b);

3.如果a是奇数,b是偶数,那么gcd(a,b)=gcd(a,b/2);

4.如果a和b都是奇数,那么gcd(a,b)=gcd(b,a mod b)。

因为大整数中包含的2是很多的,我们把这些2去掉,就可以大大减少运算量。

有个mod怎么办?我们可以把辗转相除法改成更减相损术,即把“mod”改为“-”。因为减到不能再减相当于取模,所以可以这么做。

然后我们发现,高精度只需要这样三个运算:①乘2;②除以2;③减法。只有乘2操作是让数变大的,而压9位时,最大的999999999乘2也才1999999998,不会超过int,所以不需要开long long(你想压10位、11位也随便,反正我是压了9位)。

最后一个是重点,对于操作1,我们可以直接处理掉,但对于操作2、3,我们应该和操作4一起进行。例如10000000000000和9999999999999,如果我们把前者的2全部约光,你会发现减法的运算量变得很大,而我们如果每约一个立即进行减法操作,那么减法的运算量就很小了(当然不约更好)。

然后你就把这题AC了。

C++ Code:

#include<cstdio>
#include<cstring>
#define base 1000000000
using namespace std;
char s[10005];
int two=0;
struct long_long_long{
	int t[3000];
	int len;
	long_long_long(){
		memset(t,0,sizeof(t));
		len=0;
	}
	void read(){
		scanf("%s",s+1);
		s[0]=0;
		int slen=strlen(s+1),p=0,w=1;
		for(int i=slen;i;--i){
			p=p+(s[i]^'0')*w;
			w*=10;
			if(w==base){
				t[++len]=p;
				p=0;
				w=1;
			}
		}
		if(p)t[++len]=p;
		if(len==1&&t[1]==0)len=0;
	}
	inline void print(){
		if(len==0){
			puts("0");
			return;
		}
		printf("%d",t[len]);
		for(int i=len-1;i;--i)
		printf("%09d",t[i]);
		printf("
");
	}
	
}a,b;
bool cmp(){
	if(a.len!=b.len)return a.len<b.len;
	for(int i=a.len;i;--i)
	if(a.t[i]!=b.t[i])return a.t[i]<b.t[i];
	return 0;
}
void a_div2(){
	for(int i=1;i<=a.len;++i){
		if(a.t[i]&1)a.t[i-1]+=500000000;
		a.t[i]>>=1;
	}
	while(a.t[a.len]==0&&a.len>0)--a.len;
}
void b_div2(){
	for(int i=1;i<=b.len;++i){
		if(b.t[i]&1)b.t[i-1]+=500000000;
		b.t[i]>>=1;
	}
	while(b.t[b.len]==0&&a.len>0)--b.len;
}
void a_mul2(){
	for(int i=1;i<=a.len;++i){
		a.t[i]<<=1;
		a.t[i]+=a.t[i-1]/base;
		a.t[i-1]%=base;
	}
	if(a.t[a.len]>=base){
		a.t[a.len+1]=a.t[a.len]/base;
		a.t[a.len++]%=base;
	}
}
void b_mul2(){
	for(int i=1;i<=b.len;++i){
		b.t[i]<<=1;
		b.t[i]+=b.t[i-1]/base;
		b.t[i-1]%=base;
	}
	if(b.t[b.len]>=base){
		b.t[b.len+1]=b.t[b.len]/base;
		b.t[b.len++]%=base;
	}
}
int main(){
	a.read();
	b.read();
	while(!(a.t[1]&1)&&!(b.t[1]&1)){
		a_div2();
		b_div2();
		++two;
	}
	while(1){
		while(!(a.t[1]&1))a_div2();
		while(!(b.t[1]&1))b_div2();
		if(cmp()){
			for(int i=1;i<=a.len;++i){
				if(b.t[i]<a.t[i]){
					--b.t[i+1];
					b.t[i]+=base;
				}
				b.t[i]-=a.t[i];
			}
			while(b.t[b.len]==0&&b.len>0)--b.len;
			if(!b.len){
				while(two--)a_mul2();
				a.print();
				return 0;
			}
		}else{
			for(int i=1;i<=b.len;++i){
				if(a.t[i]<b.t[i]){
					--a.t[i+1];
					a.t[i]+=base;
				}
				a.t[i]-=b.t[i];
			}
			while(a.t[a.len]==0&&a.len>0)--a.len;
			if(!a.len){
				while(two--)b_mul2();
				b.print();
				return 0;
			}
		}
	}
	return 0;
}

不想打冗长的高精?不想打超过10行的代码?

Python大法吼啊!不到100B的代码就能AC此题!

不过上述OJ中只有BZOJ支持Python,而且效率远不及手写高精。但是还是能AC的嘛。

Python Code:

a,b=input(),input()
r=a%b
while r:
 a=b
 b=r
 r=a%b
print b
原文地址:https://www.cnblogs.com/Mrsrz/p/7356625.html