zoukankan      html  css  js  c++  java
  • 逆元

    定义

    在取模意义下的运算中,加减乘法都可以进行 $$ax%p=a%p imes x%p $$ 这样的结合操作,然而除法不能这么做。那么对于取模意义下的:

    [axequiv1quad(mod;p) ]

    我们就称 (x)(a)(p) 意义下的逆元,即 (inv(a))(p) 为质数)。

    这样就把除法转换成了乘法操作。

    • 逆元非常重要,很多 (OI) 数学题都建立在逆元基础上。

    逆元的求法

    费马小定理

    这个方法只有在 (p) 是质数时能用。(费马小定理成立条件)

    没学费马小定理的可以在本人 (Blog) 查看。

    由费马小定理可知:

    [egin{align} a^{p-1}equiv 1quad(mod;p) ag1\ axequiv 1quad(mod;p) ag 2 end{align}\ herefore axequiv a^{p-1}quad(mod;p)\ herefore xequiv a^{p-2}quad(mod;p) ]

    (x) 即为 (a) 的逆元,所以只需要用快速幂求即可。

    (frak{Code})

    int ksm(int x,int q=mo-2,int mo){
    	int ret=1;
    	while(q>0){
    		if(q&1) ret=(1ll*ret*x)%mo;
    		x=(1ll*x*x)%mo;
    		q>>=1;
    	}
    	return ret%mo;
    }
    

    exgcd

    学习过 (exgcd) 后,不难发现其实逆元就是在解线性同余方程。

    那么只要解:(ax+py=1) 就行了。

    (frak{Code})

    void exgcd(int a,int b,int &x,int &y){
    	if(b==0){
    		x=1,y=0;
    	}
    	else{
    		exgcd(b,a%b,y,x);
    		y-=(a/b)*x;
    	}
    }
    int main()
    {
    	scanf("%d %d",&a,&p);
    	int x,y;
    	exgcd(a,p,x,y);
    	inv=((x%p)+p)%p;
    	printf("%d",inv);
    	return 0;
    }
    

    线性推逆元

    1. (1)(n) 的逆元

    此方法只有在 (p) 是质数时能用。(逆元只有在 (i)(p) 互质时有意义,如此大范围的求逆元,只有质数能行)

    推柿子:

    [p=i imeslfloor frac{p}{i} floor+p\%i implies i imeslfloor frac{p}{i} floor+p\%iequiv 0quad(mod;p)\ implies p\%iequiv-i imeslfloor frac{p}{i} floor\ 两边同乘 inv(i) imes inv(p\%i)\ implies inv(i)equiv -lfloor frac{p}{i} floor imes inv(p\%i) ]

    这样就可以递推求逆元辽。

    (Code)

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef int int_;
    #define int long long
    
    int inv[3000050];
    int n,p;
    
    int mod(int o,int mm){
    	return ((o%mm)+mm)%mm;
    }
    
    int_ main()
    {
    	scanf("%lld %lld",&n,&p);
    	inv[1]=1;
    	printf("%d
    ",1);
    	for(int i=2;i<=n;i++){
    		inv[i]=(-p/i)*inv[p%i];
    		inv[i]=mod(inv[i],p);
    		printf("%lld
    ",inv[i]);
    	}
    	return 0;
     } 
    

    MOD: luogu P3811

    需要线性推,但是也可以练着写写上面两种求法((80 points))。

    1. (1!)(n!) 的逆元

    在打组合数时经常用到,比上面那个简单一些,由于 (inv((n-1)!)=inv(n!) imes n) ,只要求出最大的阶乘的逆元,然后倒着退回来就行辽。

    1. 求数列 (a_1)(a_n) 的逆元

    想法和 (2.) 很像,我们记录一个前缀积数组 (fac),然后求所有数的积的逆元 (inv)。之后从 (n) 向前递推,先求出 (inv(i) = fac(i-1) imes inv) ,再将 $inv $ 乘上 (a_i) 变成前 (i-1) 位的积的逆元。如此重复到 (1) 就行辽。

    MOD: luogu P5431

    这题卡常,各位粘个读优吧。

    (Code)

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    int n,p,k,inv;
    int kfac[5000005],fac[5000005],s[5000005];
    int ans;
    
    int read() {
        int x = 0; char c = getchar(); bool f = 0;
        while (c < '0' || c > '9')
            f |= c == '-', c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f ? -x : x;
    }
    
    void exgcd(int a,int b,int &x,int &y){
    	if(b==0){
    		x=1,y=0;
    	}
    	else{
    		exgcd(b,a%b,y,x);
    		y-=(a/b)*x;
    	}
    }
    
    
    
    int main()
    {
    	scanf("%d %d %d",&n,&p,&k);
    	fac[0]=1;
    	kfac[0]=1;
    	for(int i=1;i<=n;i++){
    		s[i]=read();
    		fac[i]=(1ll*fac[i-1]*s[i])%p;
    	    kfac[i]=(1ll*kfac[i-1]*k)%p;	
    	}
    	int x,y;
    	exgcd(fac[n],p,x,y);
    	inv=((x%p)+p)%p;
    	for(int i=n;i>=1;i--){
    		ans+=(1ll*kfac[i]*((1ll*inv*fac[i-1])%p)%p);
    		if(ans>=p) ans-=p;
    		inv=(1ll*inv*s[i])%p;
    	}
    	printf("%d",ans);
    	return 0;
    }
    

    [luogu P4783] 矩阵求逆

    不知到矩阵的逆的可以去学习一下线性代数

    这道题体现了逆元的另一个用法,防止出现精度问题。由于逆元把除法变成乘法,完全就不会出现精度。

    具体要用到 (Gauss-Jordan) 消元,在上面那个链接的 (P3) 后半段。

    (Code)

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define MOD 1000000007
    
    int n;
    
    int ksm(int x,int q,int mo){
    	int ret=1;
    	while(q>0){
    		if(q&1) ret=(1ll*ret*x)%mo;
    		x=(1ll*x*x)%mo;
    		q>>=1;
    	}
    	return ret%mo;
    }
    
    int mod(int o,int mo){
    	return ((o%mo)+mo)%mo;
    }
    
    struct MAT{
    	int m[401][401];
    	int nn;
    	MAT(int x){
    		nn=x;
    		for(int i=1;i<=nn;i++){
    			for(int j=1;j<=nn;j++){
    				m[i][j]=0;
    			}
    		}
    	}
    	void input(){
    		for(int i=1;i<=nn;i++){
    			for(int j=1;j<=nn;j++){
    				scanf("%d",&m[i][j]);
    			}
    		}
    	} 
    	void init(){
    		for(int i=1;i<=nn;i++){
    			for(int j=1;j<=nn;j++){
    				m[i][j]=0;
    				if(i==j) m[i][j]=1;
    			}
    		}
    	}
    	void mul(int x,int q){
    		for(int i=1;i<=nn;i++){
    			m[x][i]=(1ll*m[x][i]*q)%MOD;
    		}
    	}
    	void elm(int x,int y,int q){
    		for(int i=1;i<=nn;i++){
    			m[x][i]-=(1ll*q*m[y][i])%MOD;
    			m[x][i]=mod(m[x][i],MOD);
    		}
    	}
    	void output(){
    		for(int i=1;i<=nn;i++){
    			for(int j=1;j<=nn;j++){
    				printf("%d ",m[i][j]);
    			}
    			printf("
    ");
    		}
    	}
    };
    
    
    int main()
    {
    	scanf("%d",&n);
    	MAT A(n),B(n);
    	A.input();B.init();
    	for(int i=1;i<=n;i++){
    		if(A.m[i][i] == 0){
    			printf("No Solution");
    			return 0;
    		}
    		int inv=ksm(A.m[i][i],MOD-2,MOD);
    		A.mul(i,inv);B.mul(i,inv);
    		for(int j=1;j<i;j++){
    			int tp=A.m[j][i];
    			A.elm(j,i,tp);
    			B.elm(j,i,tp);
    		}
    		for(int j=i+1;j<=n;j++){
    			int tp=A.m[j][i];
    			A.elm(j,i,tp);
    			B.elm(j,i,tp);
    		}
    	}
    	B.output();
    	return 0;
    }
    
    

    -EOF-
  • 相关阅读:
    订单管理功能
    商品搜索(关键字模糊搜索、三级分类搜索商品)
    通用脱敏工具类
    商品评论分页查询
    winrar激活
    认识position=fixed
    关于div的居中的问题
    关于图像在网页中显示白边的现象
    jquery 禁止页面滚动-移动端
    chrome 41 空格 &nbsp;
  • 原文地址:https://www.cnblogs.com/thornblog/p/11889737.html
Copyright © 2011-2022 走看看