同余基础数论详解

本文主要讲了扩展欧几里得、(扩展)中国剩余定理。

扩展欧几里得

小结论1:当x,y为整数时,ax+by最小正整数值为gcd(a,b)

我们设ax+by最小值为s,对应不定方程的解为x0,y0

可以发现gcd(a,b)|ax0,gcd(a,b)|by0

那么gcd(a,b)|ax0+by0=s

再设a÷s=pq(0q<s)

a=ps+q=p(ax0+by0)+q

可得q=a(1px0)+b(py0),是ax+by的形式

而且sax+by最小正整数值,q为整数,0q<s

可得q=0s|a

同理可得s|b,那么s|gcd(a,b)

而且上面也推出gcd(a,b)|s最后得到s=gcd(a,b)

小结论2:当a,b,c为整数时,ax+by=c有整数解的充分必要条件是gcd(a,b)|c

充分性:由小结论1得,ax+by=gcd(a,b)有整数解,只需将解乘上相应的倍数即是ax+by=c的解。

必要性:gcd(a,b)|a,gcd(a,b)|bgcd(a,b)|ax+by=c

扩展欧几里得

我们可以发现,gcd(a,b)=gcd(b,a%b),那我们可以试试找出ax+by=gcd(a,b)bx+(a%b)y=gcd(a,b)的解的关系。

首先有ax+by=gcd(a,b)=bx+(a%b)y

a%b=abab代入后整理一下得到

ax+by=ay+b(xaby)

可以得到其中一组解为{x=yy=xaby

代码递归到b=0时,方程就变为ax+0y=gcd(a,0),一组解就是x=1,y=0,还可以顺便求gcd(a,b)

void exgcd(ll a,ll b,ll&x,ll&y,ll&gcd) {
	if (!b) gcd=a,x=1,y=0;
	else exgcd(b,a%b,y,x,gcd),y-=a/b*x;
}

有特殊要求的解不定方程

P5656 【模板】二元一次不定方程 (exgcd)

这题比普通的不定方程多了一些要求,其中输出-1当且仅当gcd(a,b)不是c的因数,所以我们这里只讨论有解的情况。

由于方程ax+by=cagcd(a,b)x+bgcd(a,b)y=cgcd(a,b)解相同,下面就只考虑gcd(a,b)=1的情况。

我们可以看一下不定方程解的性质:

比如我们已经用exgcd求出一组解(x0,y0)

可以发现当t为任意整数时,a(x0+tb)+b(y0ta)=c(把括号拆开就是ax0+by0=c

所以这个方程的通解为{x=x0+tby=y0ta

假设使y也为正整数的x的最小正整数值为x1,最大正整数值为x2

根据通解的形式,x1,x1+b,x1+2b,,x2都对应一组正整数解。

也就是正整数解得个数为x2x1b+1

我们可以先求出x的最小正整数值:

由通解的形式得:x=x0+tb,则x最小正整数值x1就是x0%b,这样我们同事还可以求出最大值y1=cax1b

同理y最小值y2就是y0%by1%bx2=cbx2a

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void exgcd(ll a,ll b,ll&x,ll&y,ll&gcd) {
	if(!b) gcd=a,x=1,y=0;
	else exgcd(b,a%b,y,x,gcd),y-=a/b*x;
}
ll a,b,c,d,x,y,x2,y2,t,z;
int main() {
	scanf("%d",&t);
	while(~scanf("%lld%lld%lld",&a,&b,&c)) {
		exgcd(a,b,x,y,d);
		if(c%d) {//无解
			printf("-1\n");
			continue;
		}
		a/=d,b/=d,c/=d,x*=c,y*=c;//先求出x0,y0,除以gcd后才是我们讨论的情况
		x=x%b>0?x%b:x%b+b,y=(c-a*x)/b;//算出x1,y1
		y2=y%a>0?y%a:y%a+a,x2=(c-b*y2)/a;//算出x2,y2
		if(y>0) {
			printf("%lld %lld %lld %lld %lld\n",(x2-x)/b+1,x,y2,x2,y);
		} else {
			printf("%lld %lld\n",x,y2);
		}
	}
	return 0;
}

同余方程

axc(modb)转换成ax+by=c就可以解了。

由小结论2得,若gcd(a,b)|c,则有整数解,否则无整数解。

有解的时候,先解ax+by=gcd(a,b)的解,再将解乘上cgcd(a,b)即可

例题:P1082 [NOIP2012 提高组] 同余方程

有解说明gcd(a,b)|1,那么gcd(a,b)=1,直接解同余方程即可

注:这其实就是求ab意义下的逆元,逆元相关见逆元与(扩展)欧拉定理

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a,mod,x,y,d;
void exgcd(ll a,ll b,ll&x,ll&y,ll&gcd) {
	if (!b) gcd=a,x=1,y=0;
	else exgcd(b,a%b,y,x,gcd),y-=a/b*x;
}
int main() {
	scanf("%lld%lld",&a,&mod);
	exgcd(a,mod,x,y,d);
	printf("%lld\n",(x%mod+mod)%mod);//记得取模
	return 0;
}

例题:P1516 青蛙的约会

题目即求x+kmy+kn(modl)k

整理下式子就是k(mn)yx(modl)

直接解同余方程就行了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll m,n,x,y,b,c,a,gcd;
ll exgcd(ll a,ll b,ll&x,ll&y,ll&gcd) {
	if(!b)x=1,y=0,gcd=a;
	else exgcd(b,a%b,y,x,gcd),y-=a/b*x;
}
int main() {
	scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&b),a=m-n,c=y-x;
	if(a<0)a=-a,c=-c;
	exgcd(abs(a),b,x,y,gcd);
	if(c%gcd)puts("Impossible");
	else printf("%lld\n",(c/gcd*x%(b/gcd)+b/gcd)%(b/gcd));
	return 0;
}

多组同余方程的通解

例题,三倍经验:(T3需要改下读入)

P1495 【模板】中国剩余定理(CRT)/曹冲养猪

P4777 【模板】扩展中国剩余定理(EXCRT)

P3868 猜数字

现在比如要求{xa1(modb1)xan(modbn)的最小正整数解

我们假设前n1个方程的最小正整数解为slcm(b1,b2,,bn1)=m

那么我们可以得到前n1个方程的所有解都可以表示成s+xmx为整数)

现在我们就是要找一个x使s+xm也满足第n个方程

那么s+xman(modbn)xmans(modbn)

转换不定方程mx+bny=ans

如果ancgcd(m,bn)不为整数,则整个方程都无解,否则用扩展欧几里得算出x后算出s+xm即可。

也就是说,ss+xm,mm×bngcd(m,bn)就能将第n个方程也加进去。

我们只要让s初始值为a1m初始值为b1,之后把后面n1个方程逐个加入即可。

注:x一定要对bngcd(m,bn)取模,因为会乘上m,还要对lcm(m,bn)=bngcd(m,bn)m取模,对答案虽不会有影响,但下面的代码的***处可能导致精度溢出

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
void exgcd(ll a,ll b,ll&x,ll&y,ll&gcd) {
	if (!b) gcd=a,x=1,y=0;
	else exgcd(b,a%b,y,x,gcd),y-=a/b*x;
}
ll mul(ll ai,ll bi,ll p) {//快速计算a*b%p(保证精度)
	ll ansp=0,x=(ai+p)%p;
	bi=(bi+p)%p;
	while(bi!=0) {
		if(bi&1)ansp=(ansp+x)%p;
		x=(x+x)%p;
		bi>>=1;
	}
	return (ansp%p+p)%p;
}
ll mod,ans,x,y,b,c,d,a,m;
int main() {
	scanf("%d",&n);
	scanf("%lld%lld",&mod,&ans);//第一个方程的解即是a1
	for(int i=1; i<n; ++i) {
		scanf("%lld%lld",&m,&a);
		exgcd(mod,m,x,y,d);
		b=ans-a,c=m/d;//b取了相反数,后面就用了减
		if(b%d) {//判断无解
			ans=-1;
			break;
		}
		x=(mul(b/d,x,c)+c)%c;
		ans-=x*mod;// ***
		mod*=c;//计算最小公倍数
		ans=(ans%mod+mod)%mod;
	}
	printf("%lld\n",ans);
	return 0;
}
posted @   mod998244353  阅读(580)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
Live2D
欢迎阅读『同余基础数论详解』
点击右上角即可分享
微信分享提示