题目: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