高精度压位

压位的原因

正常的高精度计算中,每一位只存了一位数字,可是当面对比较大的计算的时候呢,如果说每一位都只存一位数字,那么计算的时间就会比较地长。这个时候可以通过每一位高精度中存储多位数字的方法来降低运算的时间

例题引入

简单的来说就是 一个高精度的开根号,只要求开到整数向下取整就可以了。数据范围是10^1000;
首先就是开高精度根号的方法,很容易想到的方法就是二分或者是手动开根号。我这里使用的是二分的方法。
这一道题最开始我是直接高精度来的,然后TLE了,接着我压了四位,还是TLE了,然后直接10000000一位了,终于A了。
那么接下来我来一点点解析代码(实际上我只解析读入而已了)

inline void input(){ // 读入
        char S[maxn];
        scanf("%s",S);
        memset(val,0,sizeof(val));
        int lenS=strlen(S);
        int j=0;
        for(int i=lenS-1;i>=0;i-=7){//因为是10^8的进制,所以给7位
            int t=max(i-6,0),res=0;//i-6~i一共七位,
            while(t<=i){
                res=res*10+S[t]-'0';
                t++;
            }
            val[++j]=res;
        }
        val[0]=j;
    }

我的解析全写注释了,实际上总共我也只有两句解析,因为我在这两个地方没怎么注意,所以最开始我……
至于这一个高精度的其他部分,那么就直接把10改成对应的大小就可以了,我这里是10^8

#include<bits/stdc++.h>
#define Int64 long long
#define carries 10000000
#define maxn 1003
using namespace std;
struct largenum{
    Int64 val[maxn];
    Int64 operator [](const int &ref)const{
        return val[ref];
    }

    inline void input(){ // 读入
        char S[maxn];
        scanf("%s",S);
        memset(val,0,sizeof(val));
        int lenS=strlen(S);
        int j=0;
        for(int i=lenS-1;i>=0;i-=7){
            int t=max(i-6,0),res=0;
            while(t<=i){
                res=res*10+S[t]-'0';
                t++;
            }
            val[++j]=res;
        }
        val[0]=j;
    }

    /*inline void StrIn(char *S){
        memset(val,0,sizeof(val));
        int lenS=strlen(S);
        val[0]=lenS;
        for(int i=lenS-1,j=1;i>=0;i--,j++) val[j]=S[i]-'0';
    }*/

    inline void output(){ // 输出
        printf("%ld",val[val[0]]);
        for(int i=val[0]-1;i>=1;i--)
         printf("%07ld",val[i]);
    }

    bool operator ==(const largenum &obj)const{ // 判断是否等于
        if(val[0]!=obj[0]) return false;
        for(int i=1;i<=val[0];i++) if(val[i]!=obj[i]) return false;
        return true;
    }

    bool operator <(const largenum &obj)const{ // 判断是否小于
        if(val[0]>obj[0]) return false;
        if((*this)==obj) return false;
        if(val[0]<obj[0]) return true;
        for(int i=val[0];i>=1;i--){
            if(val[i]>obj[i]) return false;
            if(val[i]<obj[i]) return true;
        }
        return true;
    }

    bool operator >(const largenum &obj)const{ // 判断是否大于
        largenum cmp=*this;
        if(cmp < obj || cmp == obj) return false;
        return true;
    }

    largenum operator +(const largenum &obj)const{ // 加法运算
        largenum cmp;
        memset(cmp.val,0,sizeof(cmp.val));
        Int64 pos=max(val[0],obj.val[0]),add=0;
        for(int i=1;i<=pos;i++){
            cmp.val[i]=val[i]+obj[i]+add;
            add=cmp.val[i]/carries;
            cmp.val[i]=cmp.val[i]%carries;
        }
        if(add>0) cmp.val[++pos]=add;
        cmp.val[0]=pos;
        return cmp;
    }

    largenum operator -(const largenum &obj)const{ // 减法运算 只能减出正数
        largenum cmp;
        memset(cmp.val,0,sizeof(cmp.val));
        Int64 pos=val[0],rent=0; // rent 借位
        for(int i=1;i<=pos;i++){
            cmp.val[i]=val[i]-obj[i]-rent;
            if(cmp.val[i]<0) {cmp.val[i]+=carries;rent=1;}
            else rent=0;
        }
        while(cmp.val[pos]==0 && pos>1) pos--;
        cmp.val[0]=pos;
        return cmp;
    }

    largenum operator *(const int &obj)const{ // 高精度 ×低精度 

        largenum cmp;
        memset(cmp.val,0,sizeof(cmp.val));
        Int64 pos=val[0]; // 进位
        long long add=0;
        for(int i=1;i<=pos;i++){
            cmp.val[i]=val[i]*obj+add;
            add=cmp.val[i]/carries;
            cmp.val[i]=cmp.val[i]%carries;
        }
        while(add>0){
            cmp.val[++pos]=add%carries;
            add/=carries;
        }
        while(cmp.val[pos]==0 && pos>1) pos--;
        cmp.val[0]=pos;
        return cmp;
    }

    largenum operator *(const largenum &obj)const{ // 高精度 ×高精度
        // 对于高精度数 a 和高精度 b
        // 这个算法不能写成 a=a*b
        largenum cmp;
        memset(cmp.val,0,sizeof(cmp.val));
        Int64 pos=val[0]+obj[0];
        for(int i=1;i<=val[0];i++){
            for(int j=1;j<=obj[0];j++){
                cmp.val[i+j-1]+=val[i]*obj[j];
                cmp.val[i+j]+=cmp.val[i+j-1]/carries;
                cmp.val[i+j-1]=cmp.val[i+j-1]%carries;
            }
        }
        while(cmp.val[pos]==0 && pos>1) pos--;
        cmp.val[0]=pos;
        return cmp;
    }

    largenum operator /(const int &obj)const{ // 高精度 ÷低精度
        largenum cmp;
        memset(cmp.val,0,sizeof(cmp.val));
        Int64 pos=val[0],div=0;
        for(int i=pos;i>=1;i--){
            cmp.val[i]=(div*carries+val[i])/obj;
            div=(div*carries+val[i])%obj;
        }
        while(cmp[pos]==0 && pos>1) pos--;
        cmp.val[0]=pos;
        return cmp;
    }

    int operator %(const int &obj)const{ // 高精度 % 低精度
        int pos=val[0],div=0;
        for(int i=pos;i>=1;i--) div=(div*carries+val[i])%obj;
        return div;
    }

    largenum operator /(const largenum &obj)const{ // 高精度 ÷高精度
        largenum cmp,t_cmp;
        memset(cmp.val,0,sizeof(cmp.val));
        memset(t_cmp.val,0,sizeof(t_cmp.val));
        Int64 pos=val[0];
        cmp.val[0]=1;cmp.val[1]=0;
        t_cmp=cmp;
        if((*this)<obj) return cmp; // 小于除数直接返回 0
        for(int i=pos;i>=1;i--){
            t_cmp=t_cmp*carries;
            t_cmp.val[1]=val[i];
            int k=0;
            while(t_cmp>obj || t_cmp==obj)
            {
                t_cmp=t_cmp-obj;
                k++;
            }
            cmp.val[i]=k;
        }
        while(cmp.val[pos]==0 && pos>1) pos--;
        cmp.val[0]=pos;
        return cmp;
    }

    largenum operator %(const largenum &obj)const{ // 高精度 % 高精度
        largenum t_cmp;
        memset(t_cmp.val,0,sizeof(t_cmp.val));
        Int64 pos=val[0];
        t_cmp.val[0]=1;t_cmp.val[1]=0;
        if((*this)<obj) return (*this); // 小于除数直接返回本身
        for(int i=pos;i>=1;i--){
            t_cmp=t_cmp*carries;
            t_cmp.val[1]=val[i];
            while(t_cmp>obj || t_cmp==obj) t_cmp=t_cmp-obj;
        }
        return t_cmp;
    }
    void lgnsqrt(){
        largenum one={{1,1}};
        largenum l={{1,1}};
        largenum r=*this;
        largenum cmp=*this;
        largenum mid=(l+r)/2;
        largenum tmp=mid*mid;
        while(l<r){
            if(tmp<cmp||tmp==cmp){
                l=mid+one;
            }
            else r=mid;
            mid=(l+r)/2;
            tmp=mid*mid;
        }
        if(mid*mid>cmp&&((mid-one)*(mid-one)<cmp||(mid-one)*(mid-one)==cmp))
            mid=mid-one;
        for(int i=0;i<=mid.val[0];i++){
            val[i]=mid.val[i];
        }
    }   

在这一段高精度的最后是自己写的二分开方这里就按正常的开方来就可以了,因为是向下取整,所以呢我就在在后面加了一个if语句。
那么,That‘s all.

原文地址:https://www.cnblogs.com/perisino/p/10335145.html

时间: 2024-08-02 10:36:53

高精度压位的相关文章

【原创】高精度(压位储存)模板

无聊写了个高精度模板玩玩...... 1 /* 2 高精度(压位储存) 3 */ 4 #include <cstdlib> 5 #include <iostream> 6 #include <cstdio> 7 #include <cstring> 8 #include <cmath> 9 const int MAX=10005;//最长长度 10 using namespace std; 11 //高精度结构体,先声明后实现 12 struct

高精度——压位的思想及应用

本文作者frankchenfu,blogs网址http://www.cnblogs.com/frankchenfu/,转载请保留此文字. 这里我们简单介绍一下高精度的计算. 我们都知道在Cpp/C/Pas等语言中,整数最大能储存\(2^{64} -1\),超过这个范围就表示不了了(不包括个别支持int128的编译器).这个时候,我们如果希望把这些整数存储下来,就需要用到高精度的算法和思想.高精度就是像小学学过的竖式运算一样的(除法除外).然后就直接模拟即可.除法一位一位地试商即可. 接下来我们发

用到了卡特兰数的性质,还有高精度压位,筛法找素数

一列火车n节车厢,依次编号为1,2,3,-,n. 每节车厢有两种运动方式,进栈与出栈,问n节车厢出栈的可能排列方式有多少种. 输入格式 输入一个整数n,代表火车的车厢数. 输出格式 输出一个整数s表示n节车厢出栈的可能排列方式数量. 数据范围 1≤n≤60000 输入样例: 3 输出样例: 5 这道题的本质是卡特兰数 卡特兰数介绍(引用math73) 筛法求素数 最重要的是如何求解组合数,压位思想,还有组合数C(2n)(n)这个式子展开以后,用上下同时除以连续的质数的方法,将答案一点一点凑出来,

codevs 3119 高精度练习之大整数开根 (各种高精+压位)

/* codevs 3119 高精度练习之大整数开根 (各种高精+压位) 二分答案 然后高精判重 打了一个多小时..... 最后还超时了...压位就好了 测试点#1.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms 测试点#2.in 结果:AC 内存使用量: 256kB 时间使用量: 1ms 测试点#3.in 结果:AC 内存使用量: 256kB 时间使用量: 0ms 测试点#4.in 结果:AC 内存使用量: 256kB 时间使用量: 10ms 测试点#5.in 结果:AC 内

[SDOI2009]SuperGCD 【压位高精度+辗转相减】

题目链接: https://www.luogu.org/problemnew/show/P2152 题目概述:  计算两个大整数(A,B)的最大公因数 数据范围 0 < A , B ≤ 10 ^ 其一在于辗转相减法--辗转相除法的优化(针对大数,避免了大数的模运算带来的多方面的复杂度) 思想就是 以数次 A-B  代替 A%B (这二者是等价的) 辗转相减法: 1 /* 2 Write(X) 输出X 3 Down(X) X除以2 4 Up(X) X乘以2 5 */ 6 void Solve(){

高精度模板2(带符号压位加减乘除开方封包)

原来的那个模板:http://www.cnblogs.com/iwtwiioi/p/3991331.html 估计已经不用了. 现在我重新封包好了一个,一定很好用QAQ 加减乘除带开方带压位带重载运算符 注意一下符号即可,一定写的时候要手推四种情况!! 然后在重载<的时候,一定要注意同时判断!!!!要不然又错.. struct big { typedef ll INT; static const INT S=100000000; static const int S_n=9; static co

[BZOJ4942][NOI2017]整数(线段树+压位)

CCF的题经常是对于一个不是非常高级的算法或数据结构挖掘性质进行优化. 松爷的题总是充满常数优化气息,这个题也确实是在常数上做文章. 首先如果全加的话是可以直接暴力的,因为可以证明对每一位来说是均摊$O(1)$的,将a二进制分解一次. 然后将暴力想下去之后容易发现,二进制加法如果进位,肯定是找到这一位之前第一个为0的位加一,然后这两位之间的所有位都变成0(就是模拟竖式加法),减法反之. 于是这个东西就是前驱查找和区间修改,应用线段树解决,$O(n\log^{2}n)$. 但是看到极其相近的数据范

压位高精

压位高精:即通过将原来数组中一个位置存一位数,改为一个位置存p位数(8-10).本质上是一种利用进制转换思想的压缩方式. 压位高精VS普通高精优势: 1.节省空间.(10倍以上) 2.节省时间(循环次数少)(10倍) 3.在对于高精与低精(乘法不超int)运算时,可以利用进制较高的优势,达到简化代码量的目的.(不需要频繁进位) 例题:洛谷 P3223 组合数学+高精乘低精. #include<cstdio> #include<algorithm> #include<iostr

线段树(压位)luogu P1558色板游戏

题目背景 阿宝上学了,今天老师拿来了一块很长的涂色板. 题目描述 色板长度为L,L是一个正整数,所以我们可以均匀地将它划分成L块1厘米长的小方格.并从左到右标记为1, 2, ... L. 现在色板上只有一个颜色,老师告诉阿宝在色板上只能做两件事: "C A B C" 指在A到 B 号方格中涂上颜色 C. "P A B" 指老师的提问:A到 B号方格中有几种颜色. 学校的颜料盒中一共有 T 种颜料.为简便起见,我们把他们标记为 1, 2, ... T. 开始时色板上原