【笔记】数论 (同余)

Exgcd(Extend Gcd):

假设我们都知道欧几里得算法,那个传说中的辗转相除法。

引入:求解方程\(ax+by=c\)\(a,b\)是整数。

首先,咱们需要一个定理:

Bézout 定理: \(ax+by=gcd(a,b)\)是保证有解的 。

证明(巨佬:显然

因为\(gcd(a,b)\)=\(gcd(b\),\(a\) \(mod\) \(b)\)

考虑到当\(b=0,a=gcd(a,b)\),那么一定有解\(x=1,y=0\)

于是我们只要证明当前\(x,y\)\(gcd(b,\)\(a\) \(mod\) \(b)\)的x,y可以推导出来就保证有解了。

那么带入:

\(bx'+\) ( \(a\) \(mod\) \(b\)) \(y'=gcd(b,\)\(a\) \(mod\) \(b\))

\(bx'+(a-a/b*b)y'=gcd(b,\)\(a\) \(mod\) \(b\))

随便移项就可以得到:

(为什么随便移? 答:写起来麻烦,手推下就晓得了。

\(ay'+b(x'-a/b*y')=gcd(b,\)\(a\) \(mod\) \(b\))\(=gcd(a,b)\)

那么\(x=y',y=(x'-a/b*y')\)

证明结束之后发现我们发现了一种求出\(ax+by=gcd(a,b)\)的x,y特解的快速办法!

那么对于\(ax+by=c\)只要先求解:\(ax+by=gcd(a,b)\)然后再把求出来的解乘上\(\frac{c}{gcd(a,b)}\)

求完一组特解。

那么通解就是:(设\(x_0,y_0\)为gcd那个式子的解

\(x=\frac{c}{gcd(a,b)}x_0+k\frac{b}{gcd(a,b)}\)

\(y=\frac{c}{gcd(a,b)}y_0-k\frac{a}{gcd(a,b)}\)

那么咱们考虑咋么用介个求解线性同余方程捏~~

先列一个方程:

\(ax \equiv b \pmod{p}\)

发现其实这方程意思是:\(ax-b\)是p的倍数,那么为什么不为了方便设成-y倍呢。

那么不就变成方程了吗:

\(ax+py=b\) 在求一遍Exgcd就解出x了~~

代码:

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

CRT(Chinese Remainder Theorom):

其实这玩意好像超强,但他实际也超强!

求同余方程的最小解(要求m互质。

\(x \equiv a_1 \pmod {m_1}\)

\(x \equiv a_2 \pmod {m_2}\)

\(x \equiv a_3 \pmod {m_3}\)

......

\(x \equiv a_k \pmod {m_k}\)

那么,打个公式吧:

\(M=\prod_{i=1}^{k}m_i\)

并且\(t_i \frac{M}{m_i} \equiv 1 \pmod {m_i}\)

那么\(x=\sum_{i=1}^{k}t_ia_i\frac{M}{m_i}\)

其实也很好理解啊
注意龟速乘

int china(){
    int ans=0,lcm=1,x,y;
    for(int i=1;i<=k;++i) lcm*=b[i];
    for(int i=1;i<=k;++i){
        int tp=lcm/b[i];
        exgcd(tp,b[i],x,y);
        x=(x%b[i]+b[i])%b[i];
        ans=(ans+tp*x*a[i])%lcm;
    }
    return (ans+lcm)%lcm;
}

ExCRT(Extend Chinese Remainder Theorom)

//M为前k个模数的最小公约数 
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,M,ans,x,y;
const int N=1e5;
int a[N+5],b[N+5];
inline int mul(int a,int b,int p){
    int ret=0;
    while(b>0){
        if(b&1) ret=(ret+a)%p;
        a=(a+a)%p;
        b>>=1;
    }
    return ret;
}
inline int Exgcd(int a,int b,int &x,int &y){
    if(b==0) { x=1;y=0;return a;}
    int tmp=Exgcd(b,a%b,x,y);
    int z=x;x=y;y=z-a/b*y;
    return tmp;
}
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;++i) 
        scanf("%lld",&b[i]),scanf("%lld",&a[i]);
    for(int i=1;i<=n;++i) a[i]=(a[i]%b[i]+b[i])%b[i];
    ans=a[1];M=b[1];
    //优先处理第一个式子的通解 
    for(int i=2;i<=n;++i){
        int oo=((a[i]-ans)%b[i]+b[i])%b[i];
        int p=Exgcd(M,b[i],x,y); 
        //求解方程x+t*M与ai同余的t的最小值 
        if(oo%p!=0) return puts("-1"),0;//无解 
        x=mul(x,oo/p,b[i]/p);//我记得在青蛙那篇博客里写到了求最小解要%... 
        ans+=x*M;//注意这几句的顺序,这里乘的是前k-1个的lcm:M 
        M*=(b[i]/p);
        ans=(ans%M+M)%M;
    }
    ans=(ans%M+M)%M;
    printf("%lld\n",ans);
    return 0;
}

欧拉定理(Euler ’ theorom) | 扩展欧拉定理(Extend Euler ’ theorom)

这个鬼东西其实挺好懂的,证明略(只是我懒,才不是我不会呢!

哼哼,事实证明我是不会证明。。。

但这并不重要!

欧拉定理:

\(a^{\phi (p)} \equiv 1 \pmod {p}\)

扩展:

\(a^b\equiv\) \(a^{b \mod\phi(p)}\pmod{p}\) \((gcd(a,p)=1)\)

\(a^b\equiv\) \(a^{b \mod\phi(p)+\phi(p)} \pmod{p}\) \((gcd(a,p)>1)\)

证明:(第一个显然,第二个不会。。。)


BSGS(Baby Step Giant Step):

坑放在这里等着明天填。。。(一不小心香了。。。)

我来填坑了~~

BSGS多用于求解高次同余方程(但普通BSGS只能解决p为质数的情况)

高次同余方程形如:

\(a^x \equiv b\pmod {p}\)

给出a,求解x。

BSGS复杂度\(O(\sqrt {p})\)

流程:

首先我们把x表示成

\(i*t-j\)\(t=(int)\sqrt{p}+1\)

那么我们把方程变为:
\(a^{i*t-j} \equiv b \pmod p\)

再来:
\(a^{i*t} \equiv b*a^{j}\pmod p\)

\((a^{t})^i\equiv b*a^j \pmod p\)

我们可以先把所有\(b*a^j\)的值插入hash

之后我们可以枚举i,然后hash中查找是否有符合条件的j,

j枚举是要小于t的,不然会发现式子变成了:\((a^t)^i \equiv b*a^t \pmod {p}\)

不知道为什么,这个式子看起来不太对,我也不知道为什么不能。。。(逃

i可以枚举到t。

现在看了那么多流程来证明一下为什么把x分解成\(i*t-j\)

根据欧拉定理推论:

\(a^b\equiv a^{b \% \phi(p)}\pmod p(gcd(a,p=1))\)

当p是个质数就把\(\phi(p)\)变成\(p-1\)

那么x要小于p-1,不然就可以变成小于等于p的x,可以模掉。

那么x只要枚举到p就好了~~

所以就是\(i*t-j<=p\),那么\(t,i,j\)最大可以取\(\sqrt{p}\)

这样就可以

代码:(代码用了拉链hash)

inline void BSGS(int a,int b,int p){
	H.clear();
	if(a%p==0) { puts("Orz, I cannot find x!");return;} 
	b%=p;
	int t=(int)sqrt(p)+1;
	for(int j=0;j<t;++j){
		int val=1ll*(b*power(a,j,p)%p)%p;
		H.insert(val,j);
	}//枚举j的取值并存入hash 
	a=power(a,t,p)%p;//先求出a^t的值 
	for(int i=0;i<=t;++i){
		int val=power(a,i,p)%p;
		int j=H.locate(val);
		if(j==-1) continue;
		if(i*t-j>=0) { printf("%lld\n",i*t-j);return;}
	}
	puts("Orz, I cannot find x!");
}
posted @ 2019-04-08 21:26  章鱼那个哥  阅读(477)  评论(0编辑  收藏  举报