同余小结

· · 个人记录

基本运算性质

(a+b)\ mod\ p=(\ (a\ mod\ p)+(b\ mod\ p)\ )\ mod\ p (a\times b)\ mod\ p=(\ (a\ mod\ p)\times(b\ mod\ p)\ )\ mod\ p

模线性方程

EX_GCD

求方程 ax+by=m 的整数解 x,y。其中 a,b,m 均为给定的整数。

1.

贝祖等式:ax+by=gcd(a,b) 一定存在整数解。

证明:

b=0 时,a=gcd(a,b)。所以 1\times a+0\times b=gcd(a,b)x=1,y=0 是方程的一组解。

根据欧几里得算法,

gcd(a,b)=gcd(b,a\ mod\ b) =b\times x_0+(a\ mod\ b)\times y_0 =b\times x_0+(a-b\times\left\lfloor\frac{a}{b}\right\rfloor)\times y_0 =b\times x_0+a\times y_0-b\times\left\lfloor\frac{a}{b}\right\rfloor\times y_0 =a\times y_0+b\times(x_0-\left\lfloor\frac{a}{b}\right\rfloor\times y_0) =a\times x+b\times y
inline int exgcd(int a,int b,int&x,int&y){
    if(!b){x=1;y=0;return a;}
    int gcd=exgcd(b,a%b,x,y);
    int z=x;x=y;y=z-a/b*y;
    return gcd;
}

2.

方程有解当且仅当 gcd(a,b)|m

所以可以先求出 ax+by=gcd(a,b) 的解,再令 x=x\times \frac{m}{gcd(a,b)}y=y\times \frac{m}{gcd(a,b)} 即可。

注意求解之前这一步一定不能漏判是否有 gcd(a,b)|m

int gcd=exgcd(a,b,x,y);
if(m%gcd)puts("No solution");
x*=m/gcd;y*=m/gcd;
printf("%d %d",x,y);

3.

k\in Zax+by=gcd(a,b),则方程的通解 xx,yy 可表示为

xx=\frac{m}{gcd}x+k\frac{b}{gcd},yy=\frac{m}{gcd}y-k\frac{a}{gcd}

4.

求该方程的非负整数解。

如果最开始求出的特解都 \ge 0,则这两个特解可直接作为答案。

如果两个特解都 \le 0,该方程无非负整数解。

否则,设大于 0 的数为 x,则 $k_{max}=\left\lfloor\frac{x\times\frac{m}{gcd}}{\frac{b}{gcd}}\right\rfloor

将 $k$ 代入 $yy$ 的通解形式后检验即可。 强调:在求非负整数解之前,一定要**判断 $gcd(a,b)|m$ 是否满足**。不满足则无解。 5. 求逆元。 $a$ 在模 $b$ 意义下的逆元 $x$ 满足 $ax\equiv 1(mod\ b)$。 这可推成方程的一个特殊形式: $$ ax\equiv 1(mod\ b)\Rightarrow gcd(a,b)=1 \Leftrightarrow ax+by=1 $$ 那么要求的就是该方程的最小非负整数解。 ```cpp inline int exgcd(int a,int b,int&x,int&y){ if(!b){x=1;y=0;return a;} int gcd=exgcd(b,a%b,x,y); int z=x;x=y;y=z-a/b*y; return gcd; } int main(){ exgcd(a,b,x,y); while(x<0)x+=b; return 0; } ``` --- ### $O(n)$ 求逆元 预处理前缀积。 ```cpp const int mod=1e9+7,N=5e6+5; int F[N],G[N],a[N];int n; inline int mul(int x,int y){return 1ll*x*y%mod;} inline int ksm(int x,int y){ int res=1; for(;y;y>>=1,x=mul(x,x)) if(y&1)res=mul(res,x); return res; } int main(){ read(n);F[0]=1; for(int i=1;i<=n;++i){ read(a[i]); F[i]=mul(F[i-1],a[i]); } G[n]=ksm(F[n],mod-2); for(int i=n-1;i;--i) G[i]=mul(G[i+1],a[i+1]); for(int i=1;i<=n;++i) printf("%d ",mul(F[i-1],G[i])); return 0; } ``` 常用来预处理阶乘 ```cpp const int mod=1e9+7,N=1e5+5; int F[N],G[N]; inline int mul(int x,int y){return 1ll*x*y%mod;} inline int ksm(int x,int y){ int res=1; for(;y;y>>=1,x=mul(x,x)) if(y&1)res=mul(res,x); return res; } int main(){ F[0]=G[0]=1; for(int i=1;i<N;++i)F[i]=mul(F[i-1],i); G[N-1]=ksm(F[N-1],mod-2); for(int i=N-2;i;--i)G[i]=mul(G[i+1],i+1); return 0; } ```