• [SinGuLaRiTy] 复习模板-数学


    【SinGuLaRiTy-1047】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.

    质因数分解

    void solve(int n)
    {
        while(n%2==0)
        {
            printf("%d*",2);
            n/=2;
        }
        for(int i=3;i<=sqrt(n);i+=2)
        {
            if(n%i==0)
            {
                n/=i;
                printf("%d*",i);
                i-=2;
            }
        }
        printf("%d
    ",n);
    }

    欧拉线性筛素数

    #define MAXN 100005
    #define MAXL 1299710
    
    int prime[MAXN];
    int check[MAXL];
    
    int tot=0;
    
    void get_prime()
    {
        for(int i=2;i<MAXL;i++)
        {
              if(!check[i])
                prime[tot++]=i;
              for(int j=0;j<tot;j++)
              {
                if(i*prime[j]>MAXL)
                    break;
                check[i*prime[j]]=1;
                if(i%prime[j]==0)
                     break;
              }
        }
    }

    筛法求欧拉函数(线性)

    #include<iostream>  
    #include<cstdio>  
    #define N 40000
    using namespace std;
    int n; int phi[N+10],prime[N+10],tot,ans; bool mark[N+10];
    void getphi() { int i,j; phi[1]=1; for(i=2;i<=N;i++)//相当于分解质因式的逆过程 { if(!mark[i]) { prime[++tot]=i;//筛素数的时候首先会判断i是否是素数。 phi[i]=i-1;//当 i 是素数时 phi[i]=i-1 } for(j=1;j<=tot;j++) { if(i*prime[j]>N)
    break; mark[i*prime[j]]=1;//确定i*prime[j]不是素数 if(i%prime[j]==0)//接着我们会看prime[j]是否是i的约数 { phi[i*prime[j]]=phi[i]*prime[j];
    break; } else
    phi[i*prime[j]]=phi[i]*(prime[j]-1);//其实这里prime[j]-1就是phi[prime[j]],利用了欧拉函数的积性 } } }

    Miller-Rabbin素数判定法

    #include<cstdlib>
    #include<ctime>
    #include<cstdio>
    
    using namespace std;
    
    const int count=10;
    
    int modular_exp(int a,int m,int n)
    {
        if(m==0)
            return 1;
        if(m==1)
            return (a%n);
        long long w=modular_exp(a,m/2,n);
        w=w*w%n;
        if(m&1)
            w=w*a%n;
        return w;
    } 
    
    bool Miller_Rabin(int n)
    {
        if(n==2)
            return true;
        for(int i=0;i<count;i++)
        {
            int a=rand()%(n-2)+2;
            if(modular_exp(a,n,n)!=a)
                return false;
        }
        return true;
    }
    
    int main()
    {
        srand(time(NULL));
        int n;
        scanf("%d",&n);
        if(Miller_Rabin(n))
            printf("Probably a prime.");
        else
            printf("A composite.");
        printf("
    ");
        return 0;
    }

    倍增求快速幂

    typedef long long ll;
    
    ll fast_power(ll a,ll b,ll c)//求a^b%c
    {
        ll ans=1;
        while(b)
        {
            if (b&1)
                ans=ans*a%c; 
            else 
                ans%=c;
            b>>=1;
            a=a*a%c; 
        }
        return ans;
    }

    大数乘法取幂

    typedef long long ll
    
    ll qmul(ll x,ll y,ll mod)// 乘法防止溢出,如果p*p不爆LL的话可以直接乘;O(1)乘法或者转化成二进制加法(快速加)
    {
        ll ret=0;
        while(y) 
        {
            if(y&1)
                ret=(ret+x)%mod;
            x=x*2%mod;
            y>>=1;
        }
        return ret;
    }
    
    ll qpow(ll a,ll n,ll mod)
    {
        ll ret=1;
        while(n)
        {
            if(n&1) 
                ret=qmul(ret,a,mod);
            a=qmul(a,a,mod);
            n>>=1;
        }
        return ret;
    }

    GCD & LCM

    LL gcd(LL a,LL b)
    {
        return b ? gcd(b,a%b) : a;
    }

    至于LCM=a*b/GCD,可能会因为a*b过大而爆掉,于是推荐使用LCM=a/GCD*b

    Exgcd

    求ax=b (mod m) ax+my=b 如果r=gcd(a,m)且b%r==0,则同余方程有解,其最小解为x*(b/r);
    ax+by=c 如r=gcd(a,b),则存在x,y,使xa+yb=r;当x+=b,y-=a后仍然成立        
    因为xa+yb+ab-ab=r;则(x+b)a+(y-a)b=r

    int exgcd(int a,int b,int &x,int &y)
    {
         if(b==0)
        {
            x=1;
            y=0;
            return a;
        }
         int r=exgcd(b,a%b,y,x);
         y-=x*(a/b);
         return r;
    }

    中国剩余定理

    设正整数m1,m2,m3...mk两两互素,则同余方程组

    有整数解。并且在模M=m1·m2·...·mk下的解是唯一的,解为

     

    其中,而的逆元。

    void extend_Euclid(int a,int b,int &x,int &y)  
    {  
        if(b==0)  
        {  
            x=1;  
            y=0;  
            return;  
        }  
        extend_Euclid(b,a%b,x,y);  
        int tmp=x;  
        x=y;  
        y=tmp-(a/b)*y;  
    }  
      
    int CRT(int a[],int m[],int n)  
    {  
        int M=1;  
        int ans=0;  
        for(int i=1;i<=n;i++)  
            M*=m[i];  
        for(int i=1;i<=n;i++)  
        {  
            int x,y;  
            int Mi=M/m[i];  
            extend_Euclid(Mi,m[i],x,y);  
            ans=(ans+Mi*x*a[i])%M;  
        }  
        if(ans<0) 
            ans+=M;  
        return ans;  
    }  

    Catalan数

    f[1]=1;
    for(int i=2;i<=n;i++)
        f[i]=f[i-1]*(4*i-2)/(i+1);

    Catalan数有许多神奇的性质,这是一篇总结的比较精炼的文章。《Catalan数——卡特兰数》

    康托展开式

    int  fac[] = {1,1,2,6,24,120,720,5040,40320}; //i的阶乘为fac[i]  
    // 康托展开-> 表示数字a是 a的全排列中从小到大排,排第几  
    // n表示1~n个数  a数组表示数字。  
    int kangtuo(int n,char a[])  
    {  
        int i,j,t,sum;  
        sum=0;  
        for( i=0;i<n;++i)  
        {  
            t=0;  
            for(j=i+1;j<n;++j)  
                if(a[i]>a[j])  
                    ++t;  
            sum+=t*fac[n-i-1];  
        }  
        return sum+1;  
    }  

    有啥用?它可以应用于哈希表中空间压缩。在码某些搜索题时,将VIS数组量压缩。比如:八数码魔板

    求乘法逆元

    exgcd,老熟人了。

    /* 
    求解ax+by=gcd(a,b),亦即ax≡1(mod b)。函数返回值是a,b的最大公约数,而x即a的逆元。 
    注意a, b不能写反了。 
    */  
    int x,y;  
    int extgcd(int a,int b,int &x,int &y)  
    {  
        if(b==0)
        {  
            x=1;  
            y=0;  
            return a;  
        }  
        int gcd=exgcd(b,a%b, x, y);  
        int tmp=x;  
        x=y;  
        y=tmp-(a/b)*y;  
        return gcd;  
    }  

    N的全排列

    #include<iostream>
    
    using namespace std;
    
    void swap(int &a,int &b)
    {
        int temp=a;
        a=b;
        b=temp;
    }
    
    void pai_xu(int a[],int m,int n)
    {
        if(m==n)
            {
            for(int i=1;i<=n;i++)
                cout<<a[i];
            cout<<endl; 
        }
        else
            for(int i=m;i<=n;i++)
                    {
                swap(a[i],a[m]);
                pai_xu(a,m+1,n);
                swap(a[i],a[m]);
            }
    }
    
    int main()
    {
        int n,m=1,a[10];
        cin>>n;
        for(int i=1;i<=n;i++)
            a[i]=i;
        pai_xu(a,m,n);
        return 0;
    }

    N的R排列

    #include<cstdio>
    #include<iostream>
    #include<cstdlib>
    #include<cmath>
    
    using namespace std;
    
    bool b[21]={0};
    int total=0,a[21]={0};
    
    bool pd(int x,int y)
    {
         int k=2,i=x+y;
         while(k<=sqrt(i)&&i%k!=0)
             k++;
         if(k>sqrt(i))
             return 1;
         else 
             return 0;
    }
    
    int print()
    {
        total++;
        cout<<total<<" "<<endl;
        for(int i=1;i<=20;i++)
            cout<<a[i]<<" ";
        cout<<endl;
    }
    
    int search(int t)
    {
        for(int i=1;i<=20;i++)
            if(pd(a[t-1],i)&&(!b[i]))
            {
                a[t]=i;
                b[i]=1;
                if(t==20)
                {
                    if(pd(a[20],a[1]))
                        print();
                }
                else 
                    search(t+1);
                b[i]=0;
            }
    }
    
    int main()
    {
        search(1);
        cout<<total<<endl;
        return 0;
    }

    有重复元素的全排列

    void fun(int n,char chars[],int flag) {  
        if(flag==n-1) {  
            for(int i=0;i<n;i++) cout<<chars[i];  
            cout<<endl;  
            return;  
        }  
        for(int i=flag;i<n;i++) {  
            if(chars[i]!=chars[flag]||i==flag){//若两个元素不相等或者两个元素的下标相同的时候才调用  
                swap(chars[i],chars[flag]);  
                fun(n,chars,flag+1);  
                swap(chars[i],chars[flag]);  
            }  
        }  
    }  

    第一类Stirling数

    定理:第一类Stirling数s(p,k)计数的是把p个对象排成k个非空循环排列的方法数。

    证明:把上述定理叙述中的循环排列叫做圆圈。递推公式为:

           s(p,p)=1 (p>=0)    有p个人和p个圆圈,每个圆圈就只有一个人

           s(p,0)=0 (p>=1)    如果至少有1个人,那么任何的安排都至少包含一个圆圈

           s(p,k)=(p-1)*s(p-1,k)+s(p-1,k-1)

           设人被标上1,2,.....p。将这p个人排成k个圆圈有两种情况。第一种排法是在一个圆圈里只有标号为p的人自己,排法有s(p-1,k-1)个。第二种排法中,p至少和另一个人在一

    个圆圈里。这些排法可以通过把1,2....p-1排成k个圆圈再把p放在1,2....p-1任何一人的左边得到,因此第二种类型的排法共有(p-1)*s(p-1,k)种排法。

           在证明中我们所做的就是把{1,2,...,p}划分到k个非空且不可区分的盒子,然后将每个盒子中的元素排成一个循环排列。

    long long s[maxn][maxn];//存放要求的第一类Stirling数  
    const long long mod=1e9+7;//取模  
      
    void init()//预处理  
    {  
        memset(s,0,sizeof(s));  
        s[1][1]=1;  
        for(int i=2;i<=maxn-1;i++)  
            for(int j=1;j<=i;j++)  
        {  
            s[i][j]=s[i-1][j-1]+(i-1)*s[i-1][j];  
            if(s[i][j]>=mod)  
                s[i][j]%=mod;  
        }  
    }  

    第二类Stirling数

    定理:第二类Stirling数S(p,k)计数的是把p元素集合划分到k个不可区分的盒子里且没有空盒子的划分个数。

    证明:元素在拿些盒子并不重要,唯一重要的是各个盒子里装的是什么,而不管哪个盒子装了什么。

            递推公式有:S(p,p)=1 (p>=0)         S(p,0)=0  (p>=1)         S(p,k)=k*S(p-1,k)+S(p-1,k-1)   (1<=k<=p-1)。考虑将前p个正整数,1,2,.....p的集合作为要被划分的集合,把

    {1,2,.....p}分到k个非空且不可区分的盒子的划分有两种情况:

           (1)那些使得p自己单独在一个盒子的划分,存在有S(p-1,k-1)种划分个数

           (2)那些使得p不单独自己在一个盒子的划分,存在有 k*S(p-1,k)种划分个数

            考虑第二种情况,p不单独自己在一个盒子,也就是p和其他元素在一个集合里面,也就是说在没有放p之前,有p-1个元素已经分到了k个非空且不可区分的盒子里面(划

    分个数为S(p-1,k),那么现在问题是把p放在哪个盒子里面呢,有k种选择,所以存在有k*S(p-1,k)。

    long long s[maxn][maxn];//存放要求的Stirling数  
    const long long mod=1e9+7;//取模  
      
    void init()//预处理  
    {  
        memset(s,0,sizeof(s));  
        s[1][1]=1;  
        for(int i=2;i<=maxn-1;i++)  
            for(int j=1;j<=i;j++)  
        {  
            s[i][j]=s[i-1][j-1]+j*s[i-1][j];  
            if(s[i][j]>=mod)  
                s[i][j]%=mod;  
        }  
    }  

    注意:要用long long类型,当元素个数>20,就超int类型了。

    扩展:k! *S(p,k) 计数的是把p元素集合划分到k个可区分的盒子里且没有空盒子的划分个数。

    组合数

    //组合数打表模板,适用于N<=3000
    //c[i][j]表示从i个中选j个的选法。
    long long C[N][N];
    void get_C(int maxn)
    {
        C[0][0] = 1;
        for(int i=1;i<=maxn;i++)
        {
            C[i][0] = 1;
            for(int j=1;j<=i;j++)
                C[i][j] = C[i-1][j]+C[i-1][j-1];
            //C[i][j] = (C[i-1][j]+C[i-1][j-1])%MOD;
        }
    }

    LUCAS定理

    long long F[100010];  
    void init(long long p)  
    {  
        F[0] = 1;  
        for(int i = 1;i <= p;i++)  
            F[i] = F[i-1]*i % (1000000007);  
    }  
    long long inv(long long a,long long m)  
    {  
        if(a == 1)return 1;  
        return inv(m%a,m)*(m-m/a)%m;  
    }  
    long long Lucas(long long n,long long m,long long p)  
    {  
        long long ans = 1;  
        while(n&&m)  
        {  
            long long a = n%p;  
            long long b = m%p;  
            if(a < b)return 0;  
            ans = ans*F[a]%p*inv(F[b]*F[a-b]%p,p)%p;  
            n /= p;  
            m /= p;  
        }  
        return ans;  
    }  

    容斥原理

    这个怎么打板呢?难道搞一道容斥的题?还是偷偷懒附个链接吧:

    《算法中的容斥原理》

    Time: 2017-11-05

  • 相关阅读:
    学会Git玩转Github笔记(一)——Github基本概念 & 仓库管理
    Github 入门基本操作
    Struts2拦截器浅析
    SSH(Struts2+Spring4+Hibernate4)框架教程之配置篇
    Spring 体系结构
    Ubuntu启动sshd服务
    jquery ajax/post 请求 案例
    Elasticsearch 配置
    Jenkins安装与配置
    解决Ubuntu环境变量错误导致无法正常登录  (command 'xxx' is available in bin ls)
  • 原文地址:https://www.cnblogs.com/SinGuLaRiTy2001/p/7787673.html
Copyright © 2020-2023  润新知