zoukankan      html  css  js  c++  java
  • Day 4 上午

    内容提要

    进制转换

    高精度

    数论:筛法,gcd/exgcd,逆元


    进制转换

    10=2^3+2^0=1010(2)
    10=3^2+3^0=101(3)

    10进制x-->k进制:短除法

    k进制x-->10进制:根据定义转换
    XnXn-1Xn-2....X0(k)
    =Xn*k^n+Xn-1*k^(n-1)+....+X0*k^0

    计算机中常用的进制:2,8,10,16

    c++中给变量赋一个二进制值:加一个前导0
    int a=01001---a=9

    十六进制int a=0x1001=16^3+16^0=4097
    十六进制中:a--10,b--11,c--12,d--13,e--14,f--15
    0x3f3f3f3f=3*16^7+15*16^6+3*16^5+...

    高精度:竖式加减乘除法
    存储:用一个数组存他的每一位(反着存)
    因为如果正着存的话数位就不能对齐了

    先写一个int的版本,然后把int全部替换为高精
    我们希望写一个好的高精模板,使得这个东西可以直接用
    面向对象的编程方法(其实就是模块化,用很多函数)

     我们先设一个结构体

    struct gaojing
    {
        int z[2333333];//用来存这个数的数组
        int l;//这个高精度是个l位的数
    };

    这里很坑的点就是对不同的编译器版本,结构体里的数会不一样
    比如dev本地的版本会全都赋值0,
    但是这显然是不对的,因为0是个n位的数
    怎么初始化?
    结构体的构造函数,就是每一次声明一个变量的时候调用一次

    struct gaojing
    {
        int z[2333333];//用来存这个数的数组
        int l;//这个高精度是个l位的数 
        gaojing()
        {
            l=1;
            memset(z,0,sizeof(z));//构造函数
         }     
    };

    已经非常接近线性筛了
    要实现什么运算?+-*/%
    <,<=,>,>=,==,!=
    cin>>,cout<<
    重载运算符
    两个高精计算,返回高精值

    gaojing operator + (gaojing a,gaojing b) 
    {
        bulabula
        return ans;
    }

    但是这样写会有问题

    (惊不惊喜,意不意外,刺不刺激

    c++再调用函数时,如果没有取地址符&
    做了一个备份再调用
    比如你设了一个x
    调用f(x)时先给x做一个备份x',然后把x'传给x
    如果加上&,就是直接把x传给它
    这里如果拷贝一个超大的高精度的数组,就有可能会tle
    所以我们加上取地址符&

    gaojing operator + (gaojing &a,gaojing &b) 
    {
        bulabulabula
        return ans;
    }

    但其实这样还有问题

    (惊不惊喜,意不意外,刺不刺激

    因为比如你手抖把a改了,那么a原本的值就会变
    所以我们再加上一个const
    而且如果你想要调用stl,就必须要加上const

     gaojing operator + (const gaojing &a,const gaojing &b) 
    {
        bulabulabula
        
        return c;
    }

    这样就好了

    完整的重载

    gaojing operator + (const gaojing &a,const gaojing &b) 
    {
        gaojing c;
        int l=max(a.l,b.l);
        for(int i=0;i<l;i++)
        {
            c.z[i]+=a.z[i]+b.z[i];
            c.z[i+1]+=c.z[i]/10;
            c.z[i]=c.z[i]%10;
        }//模拟竖式的运算过程 
        if(c.z[l]!=0)l++;//看看是否还有进位 
        c.l=l;
        
        return c;
    }

     接下来是cin和cout的重载

    cin:

    friend istream& operator>>(istream&cin,gaojing &a)
    
        {
            static char s[2333333];//static静态变量
            cin>>s;    
            int l=strlen(s);//算长度 
            for(int i=0;i<l;i++)
                a.z[i]=s[l-i-1]-'0';
            //把读进来的字符串存储 
            a.l=l;
            return cin;//要求返回cin 
        }

    其中static类似于全局变量

    不能在函数里边开数组的局部变量

    如果要开的话就要加static

    friend:友元函数 istream:读入流 ,用cin这个读入函数把它读入到a中

    用字符串读入

    第一行和最后一行当做模板记就可以

    cout:

    friend ostream& operator<<(ostream&cin,const gaojing &a)
    {
        for(int i=a.l-1;i>=0;i--)
            cout<<a.z[i];
            
        return cout;
    }

    注:这个东西要放到结构体内,因为有友元函数(虽然我不知道为什么,但是记住就好了)

    完整的就是这样:

    struct gaojing
    {
        int z[2333333];
        int l;
        gaojing()
        {
            l=1;
            memset(z,0,sizeof(z));
        }     
        friend istream& operator>>(istream&cin,gaojing &a)
    
        {
            static char s[2333333];
            cin>>s;    
            int l=strlen(s);
            for(int i=0;i<l;i++)
                a.z[i]=s[l-i-1]-'0';
            a.l=l;
            
            return cin;
        }
    
        friend ostream& operator<<(ostream&cin,const gaojing &a)
        {
            for(int i=a.l-1;i>=0;i--)
                cout<<a.z[i];
            
            return cout;
        }
    };

    下面是高精度乘法

    同样是模拟竖式计算

    gaojing operator * (const gaojing &a,const gaojing &b) 
    {
        gaojing c;
        for(int i=0;i<a.l;i++)
            for(int j=0;j<b.l;j++)
                c.z[i+j]+=a.z[i]*b.z[j];
                //第i位和第j位相乘贡献第i+j位 
                
        c.l=a.l+b.l;//位数最多是这些 
        for(int i=0;i<c.l;i++)
        {
            c.z[i+1]+=c.z[i]/10;
            c.z[i]=c.z[i]%10;
        }//处理进位 
        while(c.l>0&&c.z[c.l]==0)
            c.l--;
        c.l++;//找到第一个不等于0的位置,就是他的最高位
        //因为从0开始,所以位数+1
        
        return c; 
    }


    数论:
    π(x)≈x/log x

    质数
    只有1和自身作为因子的数叫做质数
    以π(x)表示不超过 x 的素数的个数,可以证
    lim π(x) ln x ÷ x = 1

    最常见的判断素数的方法:

    bool is_prime(int x)
    {
        if(x<=1)return false; 
        for(int a=2;a<x;a++)
            if(x%a==0) return false;
        return true; 
    }

    复杂度O(x)

    可以做一些优化

    x=a*b>=a*a,a<=b,x>=a^2,a<=√x

    bool is_prime(int x)
    {
        if(x<=1)return false; 
        for(int a=2;a*a<x;a++)
            if(x%a==0) return false;
        return true; 
    }

    复杂度降到O(√n)


    但是如果我们想要求一个区间之内的所有质数呢?
    一个一个代进去显然会tle
    复杂度O(n√n)

    所以我们需要一种更快的方法
    筛法


    两种非线性的算法:

    1.
    我们枚举所有数,并把它的所有倍数标记成合数
    复杂度O(n logn)
    n/2+n/3+n/4+....+n/n
    ≈n(1+1/2+1/3+...+1/n)
    ≈n log n

    for(a=2;a<=n;a++)
        for(int b=1+1;b<=n;b+=a)
            not_prime[b]=true; 

    2.
    实际上,我们只需要筛掉质数的倍数,因为一个合数的倍数一定是它的质因子的倍数

    for(a=2;a<=n;a++)
        if(not_prime[a]=false)
            for(int b=1+1;b<=n;b+=a)
                not_prime[b]=true; 

    复杂度O(n loglog n)

    已经非常接近线性筛了

    线性筛就是保证每个数只被他最小的质因子筛掉
    这样复杂度就降到O(n)了

    代码:

    memset(not_prime,0,sizeof(not_prime));//初始化为都是质数 
    not_prime[1]=true;//1不是质数 
    for(int i=2;i<=n;i++)
    {
        if(!not_prime[i])    prime[++prime_count]=i;
        //如果i还没有被标记为不是质数 ,就把i加到质数表里面
        for(int j=1;j<=prime_count;j++)
        {
            if(prime[j]*i>n) break;//枚举i的质数倍
            not_prime[prime[j]*i]=true;
            if(i%prime[j]==0) break;
            //如果i是第j个质数的倍数,就跳出循环 
            //因为i有prime[j]这个因子,再枚举prime[j]就会变大
            //break掉就可以保证一定是被它的最小质因子筛掉 
         } 
    }

    用来处理积性函数


    最大公因子
    gcd(a,b)=max (x|a,x|b)

    gcd(121,1001)=11

    辗转相除法
    gcd(a,b)=gcd(b,a%b)

    int gcd(int a,int b)
    {
        if(b==0) return a;//gcd(a,0)=a
        else return gcd(b,a%b);
    }

    c++自带的求最大公因数的__gcd(双下划綫),但是最好不要用

    gcd(a,b)=g
    ==>ax+by=g(x,y∈Z)

    如:
    gcd(30,12)=6
    ==>1*30-2*12=6
    ==>-1*30+3*12=6
    显然结果有无数组

    已经知道gcd(a,b)=g
    求一组ax+by=g解


    拓展欧几里得算法:

    不管进行到那一层,最大公约数都是g
    也就是在每一层都可以找到这样的x,y

    显然最后一层是最好找的
    一定有1*g+0*0=g
    然后考虑一层一层的推上去
    我们把a%b换一种写法,写成带余除法的形式
    余数=被除数-除数*商
    xb+y(a-a/b*b)=g(/是下取整)
    拆开括号合并同类项
    ya+(x-y*(a/b))b=g
    而上一层是x'a+y'b=g
    所以x'=y,y'=x-y*(a/b)

     

    int exgcd(int a,int b,int &x,int &y)
    {
        if(b==0)//到了最底层 
        {
            x=1,y=0;//这是一组解 
            return a;//最大公因数 
        }
        else 
        {
            int g=exgcd(b,a%b,x,y);
            int t=x;
            x=y,y=t-a/b*x;//x'=y,y'=x-y*(a/b)
            return g;
        }
    }

    逆元
    希望能边乘边除边取模得到模运算的正确结果

    费马小定理:
    对于质数p,任取一个1~p-1的数
    x^(p-1)%p=1
    两边同时除以x
    x^(p-2)%p=(1/x)%p
    也就是在取模意义下,a/b=a*(1/b)=a*b^(p-2)(mod p)(改符号)
    b^(p-2)就是b的逆元

    当p不是质数时,需要用到欧拉定理

    x^(φ(m))=1(mod m)改符号
    φ(m)表示1~m中有多少个数和m互质
    求φ的公式
    将m分解质因数
    m=p1^k1*p2^k2......*pn^kn
    φ(m)=m*(1-1/p1)(1-1/p2)....(1-1/pn)

    当p为质数时,φ(p)=p-1
    所以费马小定理就是欧拉定理的拓展
    当m不是质数时,除以x等于乘x^(φ(m)-1)

  • 相关阅读:
    python开源项目聚合推荐【1】
    python开发--Python实现延时操作的几种方式
    odoo开发笔记 -- 单台物理服务器上,利用docker部署多套odoo应用
    odoo开发笔记 -- odoo官方docker启动原理
    odoo开发笔记 -- odoo官方docker镜像部署
    odoo开发笔记 -- 借助模块queue_job实现异步方法调用
    odoo开发笔记 -- 提高访问安全性的一种方式
    python开发笔记-pymsslq连接操作SqlServer数据库
    运维笔记--Debian/Ubuntu系统离线安装pymssql,连接SqlServer
    python开发笔记-str转字典
  • 原文地址:https://www.cnblogs.com/lcezych/p/10799148.html
Copyright © 2011-2022 走看看