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

本文作者frankchenfu,blogs网址http://www.cnblogs.com/frankchenfu/,转载请保留此文字。

这里我们简单介绍一下高精度的计算。

我们都知道在Cpp/C/Pas等语言中,整数最大能储存\(2^{64} -1\),超过这个范围就表示不了了(不包括个别支持int128的编译器)。这个时候,我们如果希望把这些整数存储下来,就需要用到高精度的算法和思想。高精度就是像小学学过的竖式运算一样的(除法除外)。然后就直接模拟即可。除法一位一位地试商即可。

接下来我们发现就是一位一位地加减很慢,我们考虑如何把它加快速度(减小常数)。如果你有学习过关于bitset的相关知识,那你肯定对压位的方法不陌生。我们把十进制位中的每4~8位并在一起(笔者一般压4位,因为乘法时不会超过int的范围),然后照样加减,最后并不影响答案,但是要注意输出。对于除法,我们此时发现枚举\(10^4\)到\(10^8\)太慢了,注意到单调性,于是我们考虑二分试商,这样就也可以在\(log_2 BASE\)的时间之内求出来了。

注意初始化、赋值和输出。

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef int ll;

struct bign//big number
{
//init
    static const int BASE=(int)1e4,POW=4;
    static const int MAXLEN=20010;
    ll num[MAXLEN];//num[0]=length of the big number
    bign(int tp=0)
    {
        memset(num,tp,sizeof(num));
    }
    void clear()
    {
        memset(num,0,sizeof(num));
    }
    bool operator=(const char ch[])
    {
        int len=strlen(ch);
        for(int i=0;i<len;i++)
            num[(len-i-1)/POW+1]=num[(len-i-1)/POW+1]*10+ch[i]-‘0‘;
        num[0]=(len-1)/POW+1;
    }

//compare
    //big number
    bool operator<(const bign &rsh)const
    {
        if(num[0]!=rsh.num[0])
            return num[0]<rsh.num[0];
        for(int i=num[0];i;i--)
        {
            if(num[i]!=rsh.num[i])
                return num[i]<rsh.num[i];
        }
        return 0;
    }
    //int/long long
    ll max(ll _x,ll _y)
    {
        return _x>_y?_x:_y;
    }
    ll min(ll _x,ll _y)
    {
        return _x<_y?_x:_y;
    }

//operator
    //add
    void operator+=(const bign &rsh)
    {
        num[0]=max(num[0],rsh.num[0]);
        for(int i=1;i<=num[0];i++)
        {
            num[i]+=rsh.num[i];
            num[i+1]+=num[i]/BASE;
            num[i]%=BASE;
        }
        while(num[num[0]+1]>0)
            num[0]++;
    }
    bign operator+(const bign &rsh)const
    {
        bign res=*this;res+=rsh;
        return res;
    }
    //subtract
    void operator-=(const bign &rsh)
    {
        for(int i=1;i<=num[0];i++)
        {
            num[i]-=rsh.num[i];
            while(num[i]<0)
            {
                num[i]+=BASE;
                num[i+1]--;
            }
        }
        while(num[num[0]]<=0&&num[0]>0)
            num[0]--;
    }
    bign operator-(const bign &rsh)const
    {
        bign res=*this;res-=rsh;
        return res;
    }
    //multiply
    bign operator*(const bign &rsh)const
    {
        bign res;
        res.num[0]=num[0]+rsh.num[0]-1;
        for(int i=1;i<=num[0];i++)
            for(int j=1;j<=rsh.num[0];++j)
                res.num[i+j-1]+=num[i]*rsh.num[j];
        for(int i=1;i<=res.num[0];i++)
        {
            res.num[i+1]+=res.num[i]/BASE;
            res.num[i]%=BASE;
        }
        while(res.num[res.num[0]+1]>0)
        {
            res.num[0]++;
            res.num[res.num[0]+1]+=res.num[res.num[0]]/BASE;
            res.num[res.num[0]]%=BASE;
        }
        return res;
    }
    void operator*=(const bign &rsh)
    {
        bign res=*this;res=res*rsh;
        *this=res;
    }
    //divide
    void operator/=(const ll &rsh)
    {
        for(int i=num[0];i>1;i--)
        {
            num[i-1]+=(num[i]%rsh*BASE);
            num[i]/=rsh;
        }
        num[1]/=rsh;
        while(num[0]>0&&num[num[0]]<=0)
            num[0]--;
    }
    bign operator/(const ll &rsh)const
    {
        bign temp=*this ;
        temp/=rsh;
        return temp;
    }

    void operator/=(const bign &rsh)
    {
        bign l,r=*this,tmp_one;tmp_one="1";
        l.num[0]=1;
        while(l<r)
        {
            bign mid=(l+r+tmp_one)/2;
            if(*this<(rsh*mid))
                r=mid-tmp_one;
            else
                l=mid;
        }
        *this=l;
    }
    bign operator/(const bign &rsh)const
    {
        bign res=*this;res/=rsh;
        return res;
    }
    //mod
    void operator%=(const bign &rsh)
    {
        bign res=*this;
        res=res-res/rsh*rsh;
        *this=res;
    }

    bign operator%(const bign &rsh)const
    {
        bign res=*this;res%=rsh;
        return res;
    }
};
ostream&operator<<(ostream &out,const bign &x)
{
    printf("%d",x.num[x.num[0]]);
    for(int i=x.num[0]-1;i;i--)
        printf("%04d",x.num[i]);
    return out;
}

原文地址:https://www.cnblogs.com/frankchenfu/p/8158941.html

时间: 2024-11-03 22:29:37

高精度——压位的思想及应用的相关文章

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

无聊写了个高精度模板玩玩...... 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

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

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

高精度压位

压位的原因 正常的高精度计算中,每一位只存了一位数字,可是当面对比较大的计算的时候呢,如果说每一位都只存一位数字,那么计算的时间就会比较地长.这个时候可以通过每一位高精度中存储多位数字的方法来降低运算的时间 例题引入 简单的来说就是 一个高精度的开根号,只要求开到整数向下取整就可以了.数据范围是10^1000; 首先就是开高精度根号的方法,很容易想到的方法就是二分或者是手动开根号.我这里使用的是二分的方法. 这一道题最开始我是直接高精度来的,然后TLE了,接着我压了四位,还是TLE了,然后直接1

[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(){

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 内

高精度模板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. 开始时色板上原