zoukankan      html  css  js  c++  java
  • 数论

    1、分数的四则运算:用结构体表示

    2、高精度计算

    3、数论

     3.1 最小公倍数 GCD

    int gcd(int a,int b){
    	//辗转相除
    	if(b==0) return a;
    	else return gcd(b,a%b); 
    } 
    int gcd(int a,int b){
    	return !b ? a:gcd(b,a%b);
    }
    

    最大公约数  LCM

    lcm(a,b)=a*b/gcd(a,b)

     3.2  快速幂

    快速幂及扩展的矩阵快速幂,快速计算a^n的值

    两种方法

    //分治 
    int fastpow(int a,int n){
    	if(n==1) return a;
    	int temp=fastpow(a,n/2);
    	if(n%2==1) return temp*temp*a;
    	else return temp*temp;
    }
    //位运算,加上取模 
    int fastpow(int a,int n,int mod){
    	int base=a;
    	int res=1;
    	while(n){
    		if(n&1) res=res*base%mod;
    		base=base*base%mod;
    		n>>=1;
    	}
    	return res;
    } 
    

    矩阵快速幂,但是难点是把递推关系转化为矩阵的关系,例如:斐波那契数列

    struct matrix{
    	int m[maxn][maxn];
    	matrix(){
    		memset(m,0,sizeof(m));
    	}
    }; 
    matrix multi(matrix a,matrix b){
    	matrix res;
    	for(int i=0;i<maxn;i++){
    		for(int j=0;j<maxn;j++){
    			for(int k=0;k<maxn;k++){
    				res.m[i][j]=(res.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
    			}
    		}
    	}
    	return res;
    }
    matrix fastm(martix a,int n){
    	matrix res;
    	for(int i=0;i<maxn;i++) 
    	res.m[i][i]=1;  //初始化为单位矩阵 就像res=1
    	while(n){
    		if(n&1) res=multi(res,a);
    		a=multi(a,a);
    		n>>=1;
    	} 
    	return res;
    }
    

      

    3.3  求素数

    普通的做法(试除法),即从2~sqrt(n)之内判断的----n<=10^12 以内可以接受   O(√N)

    判断:[2,sqrt(n)]内的所有数-----[2,sqrt(n)]内的所有素数 

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

    埃氏筛法:O(NlongNlongN)

    因为空间用到了is[maxn],所以当maxn=1e7时是可以接受的,不然空间就太大了

    优化(1)用来做筛的数到sqrt(n)就可以了(2)for(int j=i+i 可以改为j=i*i,因为前面已经筛过了

    int prime[maxn];
    bool is[maxn];
    int ans=0;
    void findd(){
    	for(int i=2;i*i<=maxn;i++){  //是< 
    		if(is[i]==0){
    			prime[ans++]=i;
    			for(int j=i*i;j<maxn;j+=i){
    				p[j]=1;
    			}
    		}
    	}
    }

    线性筛模板

    void prim(){
        for(int i=2;i<=n;i++){
            if(!vis[i]){
                prime[++ans]=i;
            }
            for(int j=1;j<=ans&&prime[j]*i<=n;j++){
                    vis[prime[j]*i]=1;
                    if(i%prime[j]==0) break;
            }
        }
    }
    

      

    大区间素数

    埃氏筛法能解决n<=1e7的问题,但是如果n更大,那么就可以扩展位大区间素数,例如求区间[a,b]内的素数,a<b<=1e12, b-a<=1e6

    先用埃氏筛法求出[2,sqrt(b)]范围内的素数,再用这些素数去筛空间[a,b]内的素数

     题目:1619: 【例 1】Prime Distance

    3.4    扩展欧几里得算法与二元一次方程的整数解

    给出整数a,b,n,问方程ax+by=n什么时候有整数解

    !!!有解的充要条件是gcd(a,b)可以整除n

    如果确定有解,那么解题方法是先找到一个可行解(x0,y0),然后用通解公式

    x=x0+bt

    y=y0-at求解,那么问题就是如何求(x0,y0)

    扩展欧几里得方法:当方程符合ax+by=gcd(a,b)时,可以用扩展欧几里得算法求(x0,y0)

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

    求出来的x,y就是所得到x0,y0,那么通解公式就是:

    x=x0+b/gcd*t   gcd=gcd(a,b)

    y=y0-a/gcd *t

    x对应的最小非负整数解为(x%(b/gcd))+b/gcd)%(b/gcd)

    那么问题ax+by=n的整数解

    利用上面的结果,步骤如下:

    (1)判断ax+by=n是否有整数解,也就是gcd(a,b)能不能整除n

    (2)扩展欧几里得求得x0,y0

    (3)在ax0+by0=gcd(a,b)两边同时乘以n/gcd(a,b)

    (4)对照ax+by=n,得到解为:

    x0'=x0*n/gcd(a,b)     y0'=y0*n/gcd(a,b)

    那么通解公式为:

    x=x0'+b/gcd*k

    y=y0'-a/gcd *k

    运用场合:(1)求解不定方程(2)求解模的逆元(3)求解同余方程

    3.5  同余与逆元

    a%m=b%m,那么a和b对m同余,m称为同余的模,记为m|(a-b),即a-b是m的整数倍,同余的符号记为a=b(mod m) (等号三横)

    逆元:给出a,m,求方程ax=1(mod m),即ax除以m的余数为1

    有解的条件是gcd(a,m)=1,即a,m互素,等价于求ax+my=1

    方程ax=1(mod m)的一个解x,称x为a模m的逆元。这样的x很多,都称为逆

    int mod_inverse(int a,int m){
    	int x,y;
    	extend_gcd(a,m,x,y);
    	return (m+x%m)%m;  //可能是负数,所以需要处理 
    }
    

      逆元的应用:除法的模,求(a/b)mod m,a,b很大,但是求除法之后再取模会损失精度

    把除法的模运算转换成了乘法模运算

    (a/b)modm=((a/b)modm)((bk)modm)=((a/b)*(bk))modm=akmodm

    一元线性同余方程

    ax=b(mod m),即ax-b是m的整数倍,得到ax+my=b,当且仅当gcd(a,m)能整除b是有解

    当gcd(a,m)=b时,能够运用扩展欧几里得方法直接求解ax+my=b

    当gcd(a,m)!=b 时,就需要运用逆元

    如果a'是a模m的逆,则a'a=1mod(m),那么在ax=b(mod m)两边同时乘以a',那么就是x=a'b(mod m)

    步骤:求解ax+my=b   同余方程为ax=b(mod m)

    (1)有解的条件:gcd(a,m)能够整除b

    (2)求ax=1(mod m)得逆元a',等价于扩展欧几里得算法求出ax+my=1

    (3)一个特解是x=a'b

    (4)代入方程ax+my=b,求解y

    中国剩余定理(孙子定理)

    https://www.cnblogs.com/wkfvawl/p/9633188.html

     但是这样的中国剩余定理有一个要求就是m1,m2,m3...mn是互素的

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    LL n;
    //中国剩余定理(孙子定理)
    //https://www.cnblogs.com/wkfvawl/p/9633188.html 
    LL aa[12],bb[12];
    void extend_gcd(LL a,LL b,LL &x,LL &y){
        if(b==0){
            x=1;
            y=0;return;
        }
        extend_gcd(b,a%b,x,y);
        LL tmp=x;
        x=y;
        y=tmp-(a/b)*y;
    } 
    LL gcd(LL x,LL y){
        if(y==0) return x;
        else return gcd(y,x%y);
    }
    
    int main(){
        scanf("%lld",&n);
        
        LL mod=1;
        for(int i=1;i<=n;i++){
            scanf("%lld %lld",&aa[i],&bb[i]);
            mod*=aa[i];
        } 
        LL ans=0;
        LL x,y;
        for(int i=1;i<=n;i++){
            LL p=mod/aa[i];
            extend_gcd(p,aa[i],x,y);
            //cout<<x<<endl;
            ans=(ans+bb[i]*x*p)%mod;
        }
        cout<<(ans+mod)%mod;
        /*   超时到怀疑人生哈哈哈哈 
        for(LL i=mm;;i++){
            bool flag=0;
            for(int j=1;j<=n;j++){
                if(!judge(i-bb[j],aa[j])) {
                    flag=1;break;
                }
            }
            if(!flag){
                cout<<i<<endl;break;
            }
        }
        */
    return 0;
    }
    View Code

    扩展中国剩余定理

    扩展中国剩余定理跟中国剩余定理没半毛钱关系,一个是用扩展欧几里得,一个是用构造

    https://www.cnblogs.com/zwfymqz/p/8425731.html

    判定有无解:(两个一次处理)

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1e5+10;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    //变形是需要判断有没有解
    //中国剩余定理的要求就是:m1,m2....mn两两互素
    //但是如果不是两两互素的话,那么就需要扩展中国剩余定理
    //https://www.cnblogs.com/zwfymqz/p/8425731.html 
    LL n;
    //x=c1(mod m1)
    LL c[maxn],m[maxn],x,y;
    LL gcd(LL a,LL b){
        if(b==0) return a;
        return gcd(b,a%b);
    }
    LL extend_gcd(LL a,LL b,LL &x,LL &y){   //不仅算了一元二次方程的解,还算出来了公约数 
        if(b==0){
            x=1;y=0;return a;
        }
        LL r=extend_gcd(b,a%b,x,y);
        LL tmp=x;
        x=y;
        y=tmp-(a/b)*y;
        return r;
    }
    LL inv(LL a,LL b){  //a在模b下的逆
        LL r=extend_gcd(a,b,x,y);  //最大公约数 
        
        while(x<0) x+=b; //取最小非负的逆 
        return x;
    } 
    int main(){
        while(~scanf("%lld",&n)){
            for(int i=1;i<=n;i++){
                scanf("%lld %lld",&m[i],&c[i]);
            }
            bool flag=0;
            for(int i=2;i<=n;i++){  //两两合并位1个,并继续扩展 
                LL m1=m[i-1],m2=m[i];
                LL c1=c[i-1],c2=c[i];
                LL t=gcd(m1,m2);
                if((c2-c1)%t!=0){
                    flag=1;break;  //必须余数为0 因为式子中要出现相除 
                }
                m[i]=(m1*m2)/t;
                c[i]=(inv(m1/t,m2/t)*(c2-c1)/t)%(m2/t)*m1+c1;
                c[i]=(c[i]%m[i]+m[i])%m[i];   //还有这一步,别忘了 
            } 
            printf("%lld
    ",flag? -1:c[n]);
        }
    return 0;
    }
    View Code

    3.6   质因子分解

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    
    const int maxn=100010;
    bool is_prime(int x){
    	if(x==1) return 0;
    	for(int i=2;i*i<=x;i++){
    		if(x%i==0) return 0;
    	}
    	return 1;
    }
    //2147483646
    int prime[maxn],ans=0;
    struct node{
    	int x,num; //用结构体存 
    }fac[10];  //10个就够了 
    void find_prime(){  //求素数表 
    	for(int i=1;i<maxn;i++){
    	if(is_prime(i)) prime[ans++]=i; 
    }
    }
    int main(){
    	find_prime();
    	int n,num=0;
    	cin>>n;
    	if(n==1) {
    		cout<<"1"<<endl;
    		return 0; //特判 
    	}
    	cout<<n<<"=";
    	int sqr=(int)(sqrt(1.0*n));
    	for(int i=0;prime[i]<=sqr&&i<ans;i++){
    		if(n%prime[i]==0){
    			fac[num].x=prime[i];
    			fac[num].num=0;
    			while(n%prime[i]==0){
    				fac[num].num++;
    				n/=prime[i];
    			}
    			num++;
    		}
    		if(n==1) break; //提前退出 
    	}
    	if(n!=1){ //最后不等于1 ,说明有一个大于sqrt(n)的因子
    	fac[num].x=n;
    	fac[num++].num=1; 
    		
    	}
    	for(int i=0;i<num;i++){
    		if(i>0) cout<<"*";
    		cout<<fac[i].x;
    		if(fac[i].num!=1) cout<<"^"<<fac[i].num;
    	}
    	return 0;
    }
    

    ps.如果进行因子分解后,得到各因子的个数位e1,e2...ek,那么因子个数就是(e1+1)*(e2+1)*...*(ek+1)

    因子之和就是(1+p1+p1^2+...+p1^e1)*(1+p2+p2^2+...p2^e2)*...*(1+pk+pk^2+...+pk^ek)

    3.7   关于n!的问题

    1、求n!中有多少个质因子p

    可以枚举,但是很慢

    n!中有(n/p+n/p^2+.....)个质因子p.

    eg.10!中有5个2^1,2个2^2,1个2^3,所以质因子2的个数位5+2+1=8个,O(logN)

    int cal(int n,int p){
    	int ans=0;
    	while(n){
    		ans+=n/p;
    		n/=p;
    	} 
    	return ans;
    } 
    

    应用:计算n!的末尾有几个0,末尾0的个数等于n!中因子10的个数,等于质因子5的个数,计算cal(n,5)就可以了

    递归版本:

    int cal(int n,int p){
    	if(n<p) return 0;
    	return n/p+cal(n/p,p); 
    }
    

    3.8   组合数的计算

    一、计算Cnm

    方法一:通过定义计算,即使用Long long也只能承受n<=20;

    方法二:递推或者递归,单次计算不会超过O(N^2),记忆化

    res[n][m]=js(n-1,m)+js(n-1,m-1)

    res[i][j]=res[i][i-j]

    long long res[67][67]; //n=67,m=33时溢出
    long long js(long long n,long long m){
    	if(m==0||m==n) return 1;
    	if(res[n][m]!=0) return res[n][m];
    	return res[n][m]=js(n-1,m)+js(n-1,m-1);
    } 
    
    //计算整张表
    void js(){
    	int n=60;
    	for(int i=0;i<=n;i++){
    		res[i][0]=res[i][i]=1;
    	}
    	for(int i=2;i<=n;i++){
    		for(int j=1;j<=i/2;j++){
    			res[i][j]=res[i-1][j]+res[i-1][j-1];
    			res[i][i-j]=res[i][j];
    		}
    	}
    } 
    

    方法三:定义式变形计算

    ans=ans*(n-m+i)/i   i=1~m、

    //根据定义式,划分为计算m次
    //n=62,m=31时溢出 long long js(long long n,long long m){ long long ans=0; for(long long i=1;i<=m;i++){ ans=ans*(n-m+i)/i; } return ans; }

    二、计算Cnmmodp

    第一种:递推或者递归

    int res[1010][1010];  //m<=n<=1000 ,对p的素性和大小没有限制
    int js(int n,int m,int p){
    	if(m==0||m==n) return 1;
    	if(res[n][m]!=0) return res[n][m];
    	return res[n][m]=(js(n-1,m)+js(n-1,m-1))%p;
    }
    

    第二种:定义式计算

    将组合数进行质因子分解,那么Cn,m=p1^c1*p2^c2...*pn^cn%p,通过快速幂计算每一组pi^ci%p,然后相乘取模就可以了

    对Cn,m进行质因子分解:遍历不超过n的所有质数,计算n!,m!,(n-m)!中分别含质因子pi的个数x,y,z,那么Cn,m中含质因子pi的个数为x-y-z

    这里就用到了前面的求n!里面的质因子p的个数cal(int n,int p)

    m<=n<=10^6,对p的素性没有限制

    int prime[maxn]; //素数表(已经得到的,埃氏筛法) 
    int js(int n,int m,int p){
    	int ans=1;
    	for(int i=0;prime[i]<=n;i++){
    		int c=cal(n,prime[i])-cal(m,prime[i])-cal(n-m,prime[i]);
    		ans=ans*binpow(prime[i],c,p)%p;  //快速幂计算prime[i]^c%p 
    	}
    }
    

      

    高次同余方程   BSGS

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    //1.y^zmod p
    //2. x*y=z(mod p)
    //3. y^x=z(mod p)
    
    LL   js1(LL y,LL z,LL p){
        LL tmp=1;
        while(z){
            if(z&1) tmp=tmp*y%p;
            y=y*y%p;
            z>>=1;
        }
        return tmp%p;
    }
    void extend_gcd(LL a,LL b,LL &x,LL &y){
            if(b==0){
            x=1;
            y=0;return;
        }
        extend_gcd(b,a%b,x,y);
        LL tmp=x;
        x=y;
        y=tmp-(a/b)*y;
    }
    LL gcd(LL a,LL b){
        if(b==0 ) return a;
        else return gcd(b,a%b);
    }
    //x*y=z(mod p)  ax=b(mod m)
    //ax+my=b
    //y*x+p*k=z  已知y,p,z 
    //求出x0之后
    //最小非负解:((x=x0*z/gcd)+p/gcd)%(p/gcd)
    void js2(LL y,LL z,LL p){
        LL A=y,B=z,M=p;
        LL xx,yy;
        extend_gcd(A,M,xx,yy);
        //gcd(a,m)%b==0
        LL d=gcd(y,p);
        if(z%d){
            printf("Orz, I cannot find x!
    ");return;
        }
        xx=((xx*z/d)%(p/d)+p/d)%(p/d);
        printf("%lld
    ",xx);
        //return (xx%M+M)%M;
    }
    //y^x=z(mod p)
    //BSGS可以解决高次同余方程:
    //https://www.cnblogs.com/kamimxr/p/11555986.html
    
    void js3(LL a,LL ans,LL p){
        map<LL,LL> myhash;
        ans%=p;
        int tmp=sqrt(p)+1;
        for(int i=0;i<tmp;i++){
            //js1其实就是快速幂 
            myhash[(ans)*js1(a,i,p)%p]=i;
        }
        a=js1(a,tmp,p)%p;
        if(a==0&&ans==0){
            printf("1
    ");return;
        }
        if(a==0&&ans!=0){
            printf("Orz, I cannot find x!
    ");
            return;
        }
        for(int i=0;i<=tmp;i++){
            if(myhash.find(js1(a,i,p))!=myhash.end()&&(i*tmp-myhash[js1(a,i,p)]>=0)){
                printf("%d
    ",i*tmp-myhash[js1(a,i,p)]);
                return;
            }
        }
        printf("Orz, I cannot find x!
    ");
    } 
    int main(){
        int t,k;
        LL y,z,p;
        while(~scanf("%d %d",&t,&k)){
            if(k==1){
                for(int i=1;i<=t;i++){
                    scanf("%lld %lld %lld",&y,&z,&p);
                    printf("%lld
    ",js1(y,z,p));
                }
            }
            else if(k==2){
                for(int i=1;i<=t;i++){
                    scanf("%lld %lld %lld",&y,&z,&p);
                    js2(y,z,p);
                }
            }
            else if(k==3){
                ////y^x=z(mod p)
                for(int i=1;i<=t;i++){
                    scanf("%lld %lld %lld",&y,&z,&p);
                    js3(y,z,p);
                }
            }
        }
    return 0;
    }
    View Code
  • 相关阅读:
    SQL Server2016 AlwaysOn无域高可用
    Windows Server 2016 无域故障转移群集
    SQL Server高可用实现方案
    oracle11g RMAN catalog的基本使用
    Oracle_Windows server ORA-01031: insufficient privileges
    MySQL MGR 单主模式下master角色切换规则
    SQL Server AlwaysOn原理简介
    DB2创建视图并授权给其他用户
    Oracle数据库用户的密码过期问题处理
    访问GitLab的PostgreSQL数据库
  • 原文地址:https://www.cnblogs.com/shirlybaby/p/12264223.html
Copyright © 2011-2022 走看看