bzoj 1213 //1213: [HNOI2004]高精度开根 二分+高精度+结构体+运算符重载

bzoj 1213 //1213: [HNOI2004]高精度开根   二分+高精度+结构体+运算符重载

bzoj 1213 //1213: [HNOI2004]高精度开根   //在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1213

//在线测评地址https://www.luogu.org/problem/P2293

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

高精度运算中,多用加减,少用乘除,若一定要用乘除,那就尽可能多用乘,少用除,否则出处处超时。2019-11-6 21:36

没有重载运算符的代码,编得凌乱,且得分不易。

//1213: [HNOI2004]高精度开根
//在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1213
//以高精度算法经验,预感此题会超越100行代码
//估计8位压位是少不了了。考虑相乘,数据可能会溢出,采用4位压位。
//估计需编写,比较大小的函数,高精度乘高精度,高精度除低精度,高精度加高精度。
//编写高精度算法,很重要的一条,每编写一个函数都要测试一遍。
//样例通过,bzoj里提交,
//Runtime_Error    1016 kb    16 ms    C++/Edit    2384 B   2019-11-5
//在https://www.luogu.org/problem/P2293提交20分。20分代码如下,尽力了。
#include <stdio.h>
#include <string.h>
#define BASE 10000
#define BIT 4
#define maxn 10020
int a[maxn/BIT],b[maxn/BIT],c[maxn/BIT],m,left[maxn],right[maxn],mid[maxn],left1[maxn],one[10];
char s[maxn];
int cmp(int *x,int *y){//x>y 1;x<y -1;x==y 0
    int i;
    if(x[0]>y[0])return 1;
    if(x[0]<y[0])return -1;
    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 read(int *x){//读取,需要多编多练
    int len,i,tmp;
    char c;
    scanf("%s",s+1);
    len=strlen(s+1);
    for(i=1;i<=len/2;i++)c=s[i],s[i]=s[len-i+1],s[len-i+1]=c;//逆序
    x[0]=(len-1)/BIT+1;
    for(i=1;i<=len;i++){//123456789
        if((i-1)%BIT==0)x[(i-1)/BIT+1]=0,tmp=1;
        x[(i-1)/BIT+1]+=(s[i]-'0')*tmp;
        tmp*=10;
    }
}
void print(int *x){
    int i;
    printf("%d",x[x[0]]);
    for(i=x[0]-1;i>=1;i--)printf("%04d",x[i]);
}
void div(int *x,int *y){//y=x/2
    int i;
    x[x[0]+1]=0;
    for(i=x[0];i>=1;i--)y[i]=(x[i+1]%2*BASE+x[i])/2;
    i=x[0];
    while(i&&!y[i])i--;//保证最后结果i>=1
    y[0]=i;
}
void add(int *x,int *y,int *z){//z=x+y
    int i;
    z[0]=x[0]>y[0]?x[0]:y[0];
    for(i=1;i<=z[0];i++){
        z[i]+=x[i]+y[i];//此处错写成z[i]=x[i]+y[i];
        z[i+1]=z[i]/BASE;//此处错写成z[i+1]+=z[i]/BASE;
        z[i]%=BASE;//此处错写成z[i]/=BASE;
    }
    if(z[i])z[0]=i;
}
void mul(int *x,int *y,int *z){//z=x*y
    int i,j;
    z[0]=x[0]+y[0];
    for(i=1;i<=x[0];i++)
        for(j=1;j<=y[0];j++){
            z[i+j-1]+=x[i]*y[j];
            z[i+j-1+1]+=z[i+j-1]/BASE;//此处错写成z[i+j-1+1]=z[i+j-1]/BASE;
            z[i+j-1]%=BASE;
        }
    i=z[0];
    while(i&&!z[i])i--;
    z[0]=i;
}
int judge(int *x){
    int f[maxn/BIT],g[maxn/BIT],i;
    memset(f,0,sizeof(f)),f[0]=1,f[1]=1;
    for(i=1;i<=m;i++){
        memset(g,0,sizeof(g));
        mul(x,f,g);
        memcpy(f,g,sizeof(g));
    }
    if(cmp(g,a)>=0)return 1;
    else return 0;
}
int main(){
    scanf("%d",&m);
    read(a),left[0]=1,left[1]=0,memcpy(right,a,sizeof(a)),one[0]=1,one[1]=1;
    while(cmp(left1,right)==-1){//left+1<right
        memset(b,0,sizeof(b));
        add(left,right,b);
        div(b,mid);
        if(judge(mid))memcpy(right,mid,sizeof(mid));
        else memcpy(left,mid,sizeof(mid));
        memset(left1,0,sizeof(left1));
        add(left,one,left1);
    }
    print(right);
}

AC代码如下

Accepted1072 kb21752 msC++/Edit3010 B

 

 

//此文https://www.cnblogs.com/lxyyyy/p/10889765.html代码,与本人比较接近,进行对照研究,
//高精度算法,编写过程中种种的不便,决定还是要重载一些运算符
//样例通过,提交AC.2019-11-6 21:25
//看懂是一回事,编写又是另一回事,struct省去了许多初始化的麻烦
#include <cstdio>
#include <cstring>
#define N 10100
#define BIT 4
#define BASE 10000
using namespace std;
int m;
struct num{
    int a[N*2/BIT];//因涉及平方,故*2少不了
    num(){
        memset(a,0,sizeof(a));
    }
    void read(){
        char s[N],c;
        int len,i,tmp;
        scanf("%s",s+1);
        len=strlen(s+1);
        for(i=1;i<=len/2;i++)c=s[i],s[i]=s[len-i+1],s[len-i+1]=c;//逆序
        a[0]=(len-1)/BIT+1;
        for(i=1;i<=len;i++){
            if((i-1)%BIT==0)a[(i-1)/BIT+1]=0,tmp=1;
            a[(i-1)/BIT+1]+=(s[i]-'0')*tmp,tmp*=10;
        }
    }
    void print(){
        int i;
        printf("%d",a[a[0]]);
        for(i=a[0]-1;i>=1;i--)printf("%0*d",BIT,a[i]);
    }
}one,two,ten,l,r,mid,b;
num operator +(const num &p,const num &q){
    num c;
    int i;
    c.a[0]=p.a[0]>q.a[0]?p.a[0]:q.a[0];
    for(i=1;i<=c.a[0];i++)c.a[i]=p.a[i]+q.a[i];
    for(i=1;i<=c.a[0];i++)c.a[i+1]+=c.a[i]/BASE,c.a[i]%=BASE;
    if(c.a[c.a[0]+1])c.a[0]++;
    return c;
}
num operator *(const num &p,const num &q){
    num c;
    int i,j;
    c.a[0]=p.a[0]+q.a[0]-1;
    for(i=1;i<=p.a[0];i++)
        for(j=1;j<=q.a[0];j++)
            c.a[i+j-1]+=p.a[i]*q.a[j];//此处错写成c.a[i+j-1]=p.a[i]*q.a[i];
    for(i=1;i<=c.a[0];i++)c.a[i+1]+=c.a[i]/BASE,c.a[i]%=BASE;
    if(c.a[c.a[0]+1])c.a[0]++;//此处错写成if(c.a[r.a[0]+1])c.a[0]++;
    return c;//漏了此句
}
num operator /(const num &p,int q){
    num c;
    int y=0,i;
    c.a[0]=p.a[0];
    for(i=p.a[0];i>=1;i--){
        y*=BASE,y+=p.a[i];
        c.a[i]=y/q,y%=q;
    }
    while(c.a[0]&&!c.a[c.a[0]])c.a[0]--;//去除前导0
    return c;
}
bool operator <(const num &p,const num &q){
    int i;
    if(p.a[0]!=q.a[0])return p.a[0]<q.a[0];
    for(i=p.a[0];i>=1;i--)
        if(p.a[i]!=q.a[i])return p.a[i]<q.a[i];
    return 0;
}
bool operator ==(const num &p,const num &q){
    int i;
    if(p.a[0]!=q.a[0])return 0;
    for(i=p.a[0];i>=1;i--)
        if(p.a[i]!=q.a[i])return 0;
    return 1;
}
num qpow(num p,int n){//快速幂
    num c=one;
    while(n){
        if(n&1)c=c*p;
        p=p*p,n>>=1;
    }
    return c;
}
int main(){
    one.a[0]=1,one.a[1]=1,two.a[0]=1,two.a[1]=2,ten.a[0]=1,ten.a[1]=10;
    scanf("%d",&m);
    b.read();
    if(m==1){
        b.print();
        return 0;
    }
    r=one;//漏了此句
    while(qpow(r,m)<b)l=r,r=r*two;//此处错写成while(qpow(r,2)<b)l=r,r=r*two;
    b=b*qpow(ten,m),l=l*ten,r=r*ten;//因精度问题,将整体扩大10倍,之后,再将整体缩小为1/10,变回原来。//此处错写成b=b*ten,l=l*ten,r=r*ten;
    while(l+one<r){
        mid=(l+r)/2;
        if(qpow(mid,m)<b)l=mid;
        else r=mid;
    }
    if(qpow(r,m)==b)r=r/10,r.print();//此处错写成b=b/10,l=l/10,r=r/10;
    else l=l/10,l.print();
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值