bzoj 1876 //1876: [SDOI2009]SuperGCD

bzoj 1876   //1876: [SDOI2009]SuperGCD   //在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1876

更多题解,详见https://blog.csdn.net/mrcrack/article/details/90228694BZOJ刷题记录

//1876: [SDOI2009]SuperGCD
//在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1876
//先回顾CGD算法
//尝试写了非高精度的GCD,测试了样例,没有问题,代码如下
 #include
int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}
int main(){
    int a,b;
    scanf("%d%d",&a,&b);
    printf("%d\n",gcd(a,b));
    return 0;
}
//a%b可以用减法来处理,当a>b就让a=a-b,若a==0,则最大公约数找到,若0 //int 数组读入数据,逆序排列,即 数组角标小的 存储数据的低位
//测试了高精度数据的读入与输出。成功后继续代码的编写。
//高精度减法+高精度比大小
//样例通过,提交Time_Limit_Exceed。2019-6-11
//此文https://www.luogu.org/problemnew/solution/P2152?page=5思路不错,摘抄如下
辗转相减求a,b的gcd其实可以优化的:
1、若a为偶数,b为奇数:gcd(a,b)=gcd(a/2,b)
2、若a为奇数,b为偶数:gcd(a,b)=gcd(a,b/2)
3、若a,b都是偶数:gcd(a,b)=2*gcd(a/2,b/2)
3、若a,b都是奇数:gcd(a,b)=gcd(a-b,b) (a>b)
然后就涉及到高精度乘单精度,高精度减高精度,高精度除单精度……
//上述除了第3点,其它都没想到,有了思路,马上编码.
//针对2编写乘与除.
//样例通过,提交Wrong_Answer。2019-6-12
//一直怀疑时*2过程中的问题,测试了
//1024 512
//512
//果然,是有问题的

//for(i=1;i<=x[0];i++)x[i]*=2;//此处写成 for(i=1;i<=x[0];i++)x[i]*=2,x[i+1]+=x[i]/10,x[i]%=10;应先乘,再处理进位。
//for(i=1;i<=x[0];i++)x[i+1]+=x[i]/10,x[i]%=10;//为了添加此举,也排查了好久程序。
//i=x[0];//漏了此句,查了好久,即忘记i的初始化了
//修改,提交Wrong_Answer。2019-6-12
//测试
//3 4
//1
//发现有问题
//排查过程中,发现是处理sub及gcd中,x,y先后顺序的问题。
//测试了(1-10,1-10)里的数据,没有问题,提交Time_Limit_Exceed
//https://www.luogu.org/recordnew/show/19793602提交AC,但上面bzoj提交是Time_Limit_Exceed
//以下代码为 洛谷AC  4647ms / 0.79MB ,bzoj  Time_Limit_Exceed 2019-6-12 15:20

//看了一堆AC的代码,多是python或是Java的赖皮代码
//听从此文https://www.cnblogs.com/zbtrs/p/7397038.html意见, 
//这道题一定要压位来处理,不然会tle,压位后的操作与没有压位差不多,只不过mod的数变了,以前是逢10进1,现在是逢n进1.
//在纸上理解了一遍,发现原来理解有误
//原以为10000位,采用10位压缩,需处理10000-10=9990其实不是
//其实处理10000/10=1000位
//最后定位为8位的压缩方案,主要是受制于2^31-1的位数,即int的位数限制
//样例通过,提交Runtime_Error
//仔细想想,也是10000/8=1250
// 而#define maxn 1010
//修改#define maxn 1260提交AC 840 kb    908 ms
//没有感到兴奋,这是努力应得的,编码完全独立。2019-6-12 20:11
//以下为AC代码。 
#include
#include
#define maxn 1260
#define BASE 100000000
#define WIDTH 8
int a[maxn],b[maxn],cnt=0;//cnt统计*2中2的阶乘数目 
char str[maxn*8];
void print(int *x){
    int i;
    printf("%d",x[x[0]]);
    for(i=x[0]-1;i>=1;i--)printf("%08d",x[i]);
    printf("\n");
}
void read(int *x){//123456789
    int i,len,offset,t,begin;
    scanf("%s",str);
    len=strlen(str);
    if(len%WIDTH==0)x[0]=len/WIDTH;
    else x[0]=len/WIDTH+1;
    offset=len%WIDTH;//偏移量 
    for(i=offset;i>=1;i--)x[1]*=10,x[1]+=str[offset-i]-'0';//x[1]=1
    if(offset)begin=2;
    else begin=1;
    for(i=offset;i     for(i=1;i<=x[0]/2;i++)t=x[i],x[i]=x[x[0]-i+1],x[x[0]-i+1]=t;//x[1]=23456789,x[2]=1 
}
int cmp(int *x,int *y){//比大小 x>y 返回1,x==y返回0,x     int i;
    if(x[0]>y[0])return 1;
    if(x[0]     if(x[0]==y[0])
        for(i=x[0];i>=1;i--)
            if(x[i]>y[i])return 1;
            else if(x[i]     return 0;//相等 
}
void sub(int *x,int *y){//高精度减法 默认x>y 这一步,程序会预处理 
    int i;
    for(i=1;i<=y[0];i++)x[i]-=y[i];
    for(i=1;i<=x[0];i++)
        if(x[i]<0)x[i]+=BASE,x[i+1]-=1;
    i=x[0];
    while(x[i]==0)i--;//处理前导0
    x[0]=i;//漏了此句。之前确实写了,在测试代码过程中,误删了,查了半天。 
}
void div(int *x){// 除2  
    int i,a=0,shang,yu;
    for(i=x[0];i>=1;i--)a*=BASE,a+=x[i],shang=a/2,yu=a%2,x[i]=shang,a=yu;//shang商 yu余数
    i=x[0];
    while(x[i]==0)i--;//处理前导0
    x[0]=i;
}
void mul(int *x){// 乘2
    int i;
    for(i=1;i<=x[0];i++)x[i]*=2;//此处写成 for(i=1;i<=x[0];i++)x[i]*=2,x[i+1]+=x[i]/10,x[i]%=10;应先乘,再处理进位。 
    for(i=1;i<=x[0];i++)x[i+1]+=x[i]/BASE,x[i]%=BASE;//为了添加此举,也排查了好久程序。 
    i=x[0];//漏了此句,查了好久,即忘记i的初始化了 
    while(x[i+1]>0)i++;
    x[0]=i; 
}
void gcd(int *x,int *y){//非高精度return b?gcd(b,a%b):a;
    int i; 
    if(cmp(x,y)==0)return;
    if(x[1]%2==0&&y[1]%2)div(x),gcd(x,y);//1、若a为偶数,b为奇数:gcd(a,b)=gcd(a/2,b)
    else if(x[1]%2&&y[1]%2==0)div(y),gcd(x,y);//2、若a为奇数,b为偶数:gcd(a,b)=gcd(a,b/2)
    else if(x[1]%2==0&&y[1]%2==0)cnt++,div(x),div(y),gcd(x,y);//3、若a,b都是偶数:gcd(a,b)=2*gcd(a/2,b/2)
    else if(x[1]%2&&y[1]%2){//排查过程中,发现此花括号内容写得很乱。 
        if(cmp(x,y)>0)sub(x,y);
        else sub(y,x);
        gcd(x,y);
    }
}
int main(){
    int i;
    memset(a,0,sizeof(a)),memset(b,0,sizeof(b));
    read(a),read(b);
    gcd(a,b);
    for(i=1;i<=cnt;i++)mul(a);
    print(a);
    return 0;
}

//1876: [SDOI2009]SuperGCD
//在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1876
//先回顾CGD算法
//尝试写了非高精度的GCD,测试了样例,没有问题,代码如下
 #include
int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}
int main(){
    int a,b;
    scanf("%d%d",&a,&b);
    printf("%d\n",gcd(a,b));
    return 0;
}
//a%b可以用减法来处理,当a>b就让a=a-b,若a==0,则最大公约数找到,若0 //int 数组读入数据,逆序排列,即 数组角标小的 存储数据的低位
//测试了高精度数据的读入与输出。成功后继续代码的编写。
//高精度减法+高精度比大小
//样例通过,提交Time_Limit_Exceed。2019-6-11
//此文https://www.luogu.org/problemnew/solution/P2152?page=5思路不错,摘抄如下
辗转相减求a,b的gcd其实可以优化的:
1、若a为偶数,b为奇数:gcd(a,b)=gcd(a/2,b)
2、若a为奇数,b为偶数:gcd(a,b)=gcd(a,b/2)
3、若a,b都是偶数:gcd(a,b)=2*gcd(a/2,b/2)
3、若a,b都是奇数:gcd(a,b)=gcd(a-b,b) (a>b)
然后就涉及到高精度乘单精度,高精度减高精度,高精度除单精度……
//上述除了第3点,其它都没想到,有了思路,马上编码.
//针对2编写乘与除.
//样例通过,提交Wrong_Answer。2019-6-12
//一直怀疑时*2过程中的问题,测试了
//1024 512
//512
//果然,是有问题的

//for(i=1;i<=x[0];i++)x[i]*=2;//此处写成 for(i=1;i<=x[0];i++)x[i]*=2,x[i+1]+=x[i]/10,x[i]%=10;应先乘,再处理进位。
//for(i=1;i<=x[0];i++)x[i+1]+=x[i]/10,x[i]%=10;//为了添加此举,也排查了好久程序。
//i=x[0];//漏了此句,查了好久,即忘记i的初始化了
//修改,提交Wrong_Answer。2019-6-12
//测试
//3 4
//1
//发现有问题
//排查过程中,发现是处理sub及gcd中,x,y先后顺序的问题。
//测试了(1-10,1-10)里的数据,没有问题,提交Time_Limit_Exceed
//https://www.luogu.org/recordnew/show/19793602提交AC,但上面bzoj提交是Time_Limit_Exceed
//以下代码为 洛谷AC  4647ms / 0.79MB ,bzoj  Time_Limit_Exceed 2019-6-12 15:20
#include
#include
#define maxn 10010
int a[maxn],b[maxn],c[maxn],cnt=0;//cnt统计*2中2的阶乘数目
char str[maxn];
void read(int *a){
    int i,len;
    scanf("%s",str+1);
    len=strlen(str+1);
    a[0]=len;
    for(i=1;i<=len;i++)a[i]=str[len-i+1]-'0';
}
int cmp(int *x,int *y){//比大小 x>y 返回1,x==y返回0,x     int i;
    if(x[0]>y[0])return 1;
    if(x[0]     if(x[0]==y[0])
        for(i=x[0];i>=1;i--)
            if(x[i]>y[i])return 1;
            else if(x[i]     return 0;//相等
}
void sub(int *x,int *y){//高精度减法 默认x>y 这一步,程序会预处理
    int i;
    for(i=1;i<=y[0];i++)x[i]-=y[i];
    for(i=1;i<=x[0];i++)
        if(x[i]<0)x[i]+=10,x[i+1]-=1;
    i=x[0];
    while(x[i]==0)i--;//处理前导0
    x[0]=i;//漏了此句。之前确实写了,在测试代码过程中,误删了,查了半天。
}
void div(int *x){// 除2  
    int i,a=0,shang,yu;
    for(i=x[0];i>=1;i--)a*=10,a+=x[i],shang=a/2,yu=a%2,x[i]=shang,a=yu;//shang商 yu余数
    i=x[0];
    while(x[i]==0)i--;//处理前导0
    x[0]=i;
}
void mul(int *x){// 乘2
    int i;
    for(i=1;i<=x[0];i++)x[i]*=2;//此处写成 for(i=1;i<=x[0];i++)x[i]*=2,x[i+1]+=x[i]/10,x[i]%=10;应先乘,再处理进位。
    for(i=1;i<=x[0];i++)x[i+1]+=x[i]/10,x[i]%=10;//为了添加此举,也排查了好久程序。
    i=x[0];//漏了此句,查了好久,即忘记i的初始化了
    while(x[i+1]>0)i++;
    x[0]=i;
}
void gcd(int *x,int *y){//非高精度return b?gcd(b,a%b):a;
    int i;
    if(cmp(x,y)==0)return;
    if(x[1]%2==0&&y[1]%2)div(x),gcd(x,y);//1、若a为偶数,b为奇数:gcd(a,b)=gcd(a/2,b)
    else if(x[1]%2&&y[1]%2==0)div(y),gcd(x,y);//2、若a为奇数,b为偶数:gcd(a,b)=gcd(a,b/2)
    else if(x[1]%2==0&&y[1]%2==0)cnt++,div(x),div(y),gcd(x,y);//3、若a,b都是偶数:gcd(a,b)=2*gcd(a/2,b/2)
    else if(x[1]%2&&y[1]%2){//排查过程中,发现此花括号内容写得很乱。
        if(cmp(x,y)>0)sub(x,y);
        else sub(y,x);
        gcd(x,y);
    }
}
int main(){
    int i;
    memset(a,0,sizeof(a)),memset(b,0,sizeof(b));
    read(a),read(b);
    gcd(a,b);
    for(i=1;i<=cnt;i++)mul(a);
    for(i=a[0];i>=1;i--)printf("%d",a[i]);printf("\n");//查了半天,才发现代码写成 for(i=a[0];i>=1;i++)printf("%d",a[i]);printf("\n");
    return 0;
}

//1876: [SDOI2009]SuperGCD
//在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1876
//先回顾CGD算法
//尝试写了非高精度的GCD,测试了样例,没有问题,代码如下
 #include
int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}
int main(){
    int a,b;
    scanf("%d%d",&a,&b);
    printf("%d\n",gcd(a,b));
    return 0;
}
//a%b可以用减法来处理,当a>b就让a=a-b,若a==0,则最大公约数找到,若0 //int 数组读入数据,逆序排列,即 数组角标小的 存储数据的低位 
//测试了高精度数据的读入与输出。成功后继续代码的编写。 
//高精度减法+高精度比大小 
//样例通过,提交Time_Limit_Exceed。2019-6-11 

//以下代码为洛谷中https://www.luogu.org/problemnew/show/P2152/64分代码,测试点7-10 TLE. bzoj中Time_Limit_Exceed
#include
#include
#define maxn 10010
int a[maxn],b[maxn],c[maxn];
char str[maxn];
void read(int *a){
    int i,len;
    scanf("%s",str+1);
    len=strlen(str+1);
    a[0]=len;
    for(i=1;i<=len;i++)a[i]=str[len-i+1]-'0';
}
int cmp(int *x,int *y){//比大小 x>y 返回1,x==y返回0,x     int i;
    if(x[0]>y[0])return 1;
    if(x[0]     if(x[0]==y[0])
        for(i=x[0];i>=1;i--)
            if(x[i]>y[i])return 1;
            else if(x[i]     return 0;//相等 
}
void sub(int *x,int *y){//高精度减法 默认x>y 这一步,程序会预处理 
    int i;
    for(i=1;i<=y[0];i++)x[i]-=y[i];
    for(i=1;i<=x[0];i++)
        if(x[i]<0)x[i]+=10,x[i+1]-=1;
    i=x[0];
    while(x[i]==0)i--;//处理前导0
    x[0]=i;//漏了此句。之前确实写了,在测试代码过程中,误删了,查了半天。 
}
void gcd(int *x,int *y){//非高精度return b?gcd(b,a%b):a;
    int i; 
    if(cmp(x,y)==0)return;
    while(cmp(x,y)>0)sub(x,y);
    gcd(y,x);
}
int main(){
    int i;
    read(a),read(b);
    if(cmp(a,b)>=0)gcd(a,b);
    else gcd(b,a);
    for(i=a[0];i>=1;i--)printf("%d",a[i]);printf("\n");//查了半天,才发现代码写成 for(i=a[0];i>=1;i++)printf("%d",a[i]);printf("\n");
    return 0;
}

你可能感兴趣的:(跟着大佬学算法)