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 <stdio.h>
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<a<b,则该次的GCD完成.2019-6-11
//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 <stdio.h>
#include <string.h>
#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<len;i++)x[begin+(i-offset)/WIDTH]*=10,x[begin+(i-offset)/WIDTH]+=str[i]-'0';//此处写成for(i=offset;i<len;i++)x[2+(i-offset)/WIDTH]*=BASE,x[2+(i-offset)/WIDTH]+=str[i]-'0';//x[2]=23456789//此处写成 x[1+i%BIT]*=10,x[1+i%BIT]=str[len-i+1]-'0';够昏
    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<y 返回-1 
    int i;
    if(x[0]>y[0])return 1;
    if(x[0]<y[0])return -1;
    if(x[0]==y[0])
        for(i=x[0];i>=1;i--)
            if(x[i]>y[i])return 1;
            else if(x[i]<y[i])return -1;
    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 <stdio.h>
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<a<b,则该次的GCD完成.2019-6-11
//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 <stdio.h>
#include <string.h>
#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<y 返回-1
    int i;
    if(x[0]>y[0])return 1;
    if(x[0]<y[0])return -1;
    if(x[0]==y[0])
        for(i=x[0];i>=1;i--)
            if(x[i]>y[i])return 1;
            else if(x[i]<y[i])return -1;
    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 <stdio.h>
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<a<b,则该次的GCD完成.2019-6-11
//int 数组读入数据,逆序排列,即 数组角标小的 存储数据的低位 
//测试了高精度数据的读入与输出。成功后继续代码的编写。 
//高精度减法+高精度比大小 
//样例通过,提交Time_Limit_Exceed。2019-6-11 

//以下代码为洛谷中https://www.luogu.org/problemnew/show/P2152/64分代码,测试点7-10 TLE. bzoj中Time_Limit_Exceed
#include <stdio.h>
#include <string.h>
#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<y 返回-1 
    int i;
    if(x[0]>y[0])return 1;
    if(x[0]<y[0])return -1;
    if(x[0]==y[0])
        for(i=x[0];i>=1;i--)
            if(x[i]>y[i])return 1;
            else if(x[i]<y[i])return -1;
    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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值