题目:BZOJ1876、洛谷P2152、codevs2305。
题目大意:给你两个数a,b($1\leq 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("\n");
}
}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