乘法逆元&矩阵求逆

乘法逆元简介

模意义下乘法运算的逆元

  • 定义:如果一个线性同余方程 \(ax \equiv 1 \pmod b\),则 \(x\) 称为 \(a \bmod b\) 的逆元,记作 \(a^{-1}\)
  • 分数取模:设\(c\)\(b\)的逆元,则有\(( a/b ) \mod m = a*c \mod m\)

费马小定理

\[\begin{array}{l} \because ax \equiv 1 \pmod{b} \\ \therefore ax \equiv a^{b-1}\pmod{b} \\ \therefore x \equiv a^{b-2}\pmod{b} \\ \end{array} \]

扩展欧几里得

OI-wiki

定理1

线性同余方程 \(ax\equiv b \pmod n\) 可以改写为如下线性不定方程:\(ax + nk = b\)
其中 \(x\)\(k\) 是未知数。
有整数解的充要条件为 \(\gcd(a,n) \mid b\)
对于线性不定方程 \(ax+nk=b\),可以先用扩展欧几里得算法求出一组 \(x_0,k_0\),也就是 \(ax_0+nk_0=\gcd(a,n)\),然后两边同时除以 \(\gcd(a,n)\),再乘 \(b\)。就得到了方程$$
a\dfrac{b}{\gcd(a,n)}x_0+n\dfrac{b}{\gcd(a,n)}k_0=b$$于是找到方程的一个解。

定理2

\(\gcd(a,n)=1\),且 \(x_0、k_0\) 为方程 \(ax+nk=b\) 的一组解,则该方程的任意解可表示为:

\[\begin{array}{c}x=x_0+nt\\k=k_0-at\end{array} \]

并且对任意整数 t 都成立。
根据定理 2,可以从已求出的一个解,求出方程的所有解。实际问题中,往往要求出一个最小整数解,也就是一个特解$$x=(x \bmod t+t) \bmod t$$
其中有$$
t=\dfrac{n}{\gcd(a,n)}$$

void exgcd(cs int a,cs int b,int &x,int &y)
{
    if (!b) return x=1,y=0,void();
    exgcd(b,a%b,y,x), y-=a/b*x;
}
int main()
{
    int x,y; exgcd(a,Mod,x,y);
    x=(x%Mod+Mod)%Mod,wt(x); //x是 a 在 mod p意义下的下的逆元
}

线性求逆元

\[{i^{-1}}\equiv \begin{cases} 1, & \text{if } i = 1,\\ -\lfloor \frac{p}{i} \def\foo{^{-1}}\rfloor\foo{{( p\mod i )}^{-1}}, & \text{otherwise.}\\ \end{cases} (\bmod p) \]

P3811 【模板】乘法逆元

il void calc()
{
    inv[1]=1;
    for(ri int i=2;i<=n;++i) 
        inv[i]=(p-p/i)*inv[p%i]%p;
}

线性求任意 \(n\) 个数的逆元

首先计算 \(n\) 个数的前缀积,记为 \(s_i\),然后使用快速幂或扩展欧几里得法计算 \(s_n\) 的逆元,记为 \(sv_n\)

因为 \(sv_n\)\(n\) 个数的积的逆元,所以当我们把它乘上 \(a_n\) 时,就会和 \(a_n\) 的逆元抵消,于是就得到了 \(a_1\)\(a_{n-1}\) 的积逆元,记为 \(sv_{n-1}\)

同理我们可以依次计算出所有的 \(sv_i\),于是
\(a_i^{-1}\) 就可以用 \(s_{i-1} \times sv_i\) 求得。

复杂度 \(O(n + \log p)\)

P5431 【模板】乘法逆元 2

点击查看代码
#include<bits/stdc++.h>
#define cs const
#define il inline
#define ri register
#define pc(i) putchar(i)
#define int long long
using namespace std;
il void read(int &as)
{
	as=0;int f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') as=(as<<3)+(as<<1)+(ch^48),ch=getchar();as*=f;
}
void wt(int x){if(x<0) x=-x,pc('-');if(x>9) wt(x/10);pc(x%10|48);}
cs int N=5e6+7;
int n,Mod,k,a[N],s[N],sv[N],ans,kk;
il int qpow(int base,int power)
{
	int re=1;
	while(power>0)
	{
		if(power&1) (re*=base)%=Mod;
		power>>=1,(base*=base)%=Mod;		
	}
	return re;
}
signed main()
{
	s[0]=1,read(n),read(Mod),read(k),kk=k;
	for(ri int i=1;i<=n;++i) 
		read(a[i]),s[i]=s[i-1]*a[i]%Mod;
	sv[n]=qpow(s[n],Mod-2);
	for(ri int i=n;i>=1;--i) 
		sv[i-1]=sv[i]*a[i]%Mod;
	for(ri int i=1;i<=n;++i)
		(ans+=kk*s[i-1]%Mod*sv[i]%Mod)%=Mod,(kk*=k)%=Mod;
	wt(ans); 
	return 0;
}

矩阵求逆

高斯消元,分数取模用上述方法转化即可

点击查看代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#define il inline
#define ri register
#define pc(i) putchar(i)
#define int long long
using namespace std;
const int Mod=1e9+7,N=500;
int n,a[N][N<<1];
il void wt(int x){if(x<0)pc('-'),x=-x;if(x>9)wt(x/10);pc(x%10+48);}
il int qpow(int b,int p){int re=1;while(p>0){if(p&1)re=re*b%Mod;p>>=1,b=b*b%Mod;}return re%Mod;} 
il void Pt(){for(ri int i=1;i<=n;++i){for(ri int j=n+1;j<=n+n;++j)printf("%lld ",a[i][j]);pc('\n');}}
il int inv(int x){return qpow(x,Mod-2)%Mod;}
il int M(int x) {return (x%Mod+Mod)%Mod;}
il void Gauss()
{
    for(ri int i=1;i<=n;++i)
    {
        int Max=i,ai;
        for(ri int j=i+1;j<=n;++j) if(abs(a[Max][i])<abs(a[j][i])) Max=j;//upd: >
        if(!a[Max][i]) return puts("No Solution"),void();
        swap(a[Max],a[i]),ai=inv(a[i][i]); 
        for(ri int j=1;j<=n;++j)
        {
            if(j==i) continue;
            int p=a[j][i]*ai%Mod;// a[j][i]/a[i][i]%Mod
            for(ri int k=1;k<=n+n;++k) a[j][k]=M(a[j][k]-p*a[i][k]);
        }
        for(ri int k=1;k<=n+n;++k) a[i][k]=a[i][k]*ai%Mod; // a[i][k]/a[i][i]%Mod
    }
    Pt();
}
signed main()
{
    scanf("%lld",&n);
    for(ri int i=1;i<=n;++i)
    for(ri int j=1;j<=n;++j)scanf("%lld",&a[i][j]),a[i][i+n]=1;
    Gauss();
    return 0;
}
posted @ 2022-10-18 10:21  Bertidurlah  阅读(182)  评论(0)    收藏  举报