zoukankan      html  css  js  c++  java
  • [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
    
  • 相关阅读:
    使用IDEA打包出现“Cleaning up unclosed ZipFile for archive”错误
    Kafka读取本地文件作为生产者
    Redis连接池
    如果javaapi长时间消费不到数据
    Redis 持久化的两种方式
    kafka结合streaming的两种方式
    二分法查找
    shell脚本学习
    Md5Utils
    idea添加jar包
  • 原文地址:https://www.cnblogs.com/Mrsrz/p/7356625.html
Copyright © 2011-2022 走看看