c++ 数论

本文探讨了费马小定理和欧拉定理的基本原理及其证明,包括欧拉函数、简化剩余系的应用,以及这些定理在拓展欧几里得和中国剩余定理中的关键角色。通过实例和代码展示了如何利用这些理论解决实际问题,如求逆元和求解不定方程。

1.一些定理及证明

 

1.费马小定理

若p为素数,则有  a^{p}\equiv a(\mod p)

2.欧拉定理

若a,p互质,则有  a^{\phi p}\equiv1(\mod p)

证明:

\phi p为p的欧拉函数

设a mod p的简化剩余系为  {a_{1},a_{2},a_{3},...a_{\phi p}}

因为a,p互质,所以a*p一定在a mod p的简化剩余系里

故  a^{\phi p}*a_{1}*a_{2}*...*a_{\phi p}=a_{1}*a_{2}*...*a_{\phi p}

所以 a^{\phi p}\equiv1(\mod p) , 故欧拉定理成立

因为p为质数,所以 \phi p=p-1

故 a^{p}\equiv a(\mod p),费马小定理成立

欧拉定理的推论:若a,p互质,则有 a^{n}\equiv a^{n%\phi p}(\mod p) 

证明:令 n%\phi p=kn=t*\phi p+k

故  a^{n}=a^{t*\phi p+k}=(a^{\phi p})^{t}*a^{k}=1^{t}*a^{k}=a^{k},推论成立

2.拓展欧几里得

1.bezout定理:一定存在整数使ax+by=gcd(a,b)

证明 当b=0时,x=1,y=0,命题成立

当b!=0时,gcd(a,b)=gcd(b,a mod b)

假设有一组解使ax+by=gcd(b,a mod b)成立

a \mod b=a-\left \lfloor a/b \right \rfloor*b

故 b*x+(a-\left \lfloor a/b \right \rfloor*b)*y=gcd(a,b)

故 a*y+b*(x-\left \lfloor a/b\right\rfloor)

所以一定有一组整数解为 x_{0}=y,y_{0}=x-\left \lfloor a/b\right\rfloor

实现代码:

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

对于一般的不定方程ax+by=d,当且仅当 gcd(a,b)|d时方程有整数解

一组整数解为  x_{0}=\frac{d}{gcd(a,b)}*x, y_{0}=\frac{d}{gcd(a,b)}*y

所有整数解为  x=x_{0}+k*\frac{b}{gcd(a,b)},y=y_{1}-k*\frac{a}{gcd(a,b)}

证明:gcd(a,b)|a,gcd(a,b)|b

所以新的x,y一定是整数

将    x=x_{0}+k*\frac{b}{gcd(a,b)}带入不定方程

因为 a*x_{0}+b*y_{0}=d

解得   y=y_{1}-k*\frac{a}{gcd(a,b)}

逆元

因为除法运算不能取模,所以需要找到一个合适的工具来代替取模运算中的除法,就引进了逆元

inv*x\equiv1(\mod m),inv就是x在模m意义下的逆元

在模m的意义下,x*inv的值是1,就相当于x*1/x,同理,k为任意整数,k*inv在模m的意义下就相当于k*1/x

当且仅当x与m互质的情况下才有逆元

证明:

\because inv*x\equiv1(\mod m)

\therefore inv*x=1-k*m

\therefore x*inv+m*k=1

根据bezout定理,当且仅当gcd(x,m)|1时方程有解

故gcd(x,m)=1,也就是x与m互质

3.中国剩余定理

 设m1,m2,m3...两两互质,且 

\begin{Bmatrix} x\equiv a_{1}(\mod m_{1})\\ x\equiv a_{2}(\mod m_{2}) \\ x\equiv a_3(\mod m_{3}) \end{Bmatrix}

求x最小值 

令   m=\prod _{i=1}^{i\leq n} m_{i},inv_{i}*\frac{m}{m_{i}}\equiv1(\mod m_{i})

则   ans=\sum _{i=1}^{i\leq n}\frac{m}{m_{i}}*inv_{i}*a[i]

证明:对于i=k时,m/m[i]保证了这个答案加上对其他项没有影响,其他保证了这一项加上后在ans对m[i]取模时答案为a[i](m/m[i]*inv在模m[i]的意义下余1,故这个式子在模m[i]的情况下为a[i]

for(int i=1;i<=n;i++){
	ll mi=mulmod/a[i];
	ll x,y;
	ll gcd=exgcd(mi,m[i],x,y);//求出在模m[i]意义下的逆元
	x=(x%m[i]+m[i])%m[i];
	ans+=mi*x*a[i];
} 

4.拓展中国剩余定理

\begin{Bmatrix} x\equiv a_{1}(\mod m_{1})\\ x\equiv a_{2}(\mod m_{2}) \\ x\equiv a_3(\mod m_{3}) \end{Bmatrix}

还是这种形式,但是模数不一定互质,这时候就不能用中国剩余定理了

因为若余数不两两互质,  m/m[i]就不会和m[i]互质,就找不到 inv_{i}*\frac{m}{m_{i}}\equiv1(\mod m_{i})

所以中国剩余定理就不再适用

这时考虑构造特解,对于  x\equiv a_i(\mod m_{i}) ,令 lcm=lcm(m_{1},m_{2},...,m_{i-1}),ans为前i-1组方程的解

那么需要找到一个x使  ans+x*lcm\equiv a_{i}(\mod m_{i}),那么ans+x*lcm一定是前i组方程的通解

因为 x*lcm\equiv 0(\mod m_{k}),ans\equiv a_{i}(\mod m_{i}),k\in[1,i-1]

就需要求出x的值,然后用ans+x*lcm更新ans

	mul=1;
	for(int i=1;i<=n;i++){
		ll gcd=exgcd(mul,b[i],x,y);
		ll d=a[i]-ans;
		x=x*(d/gcd);
		ll ty=b[i]/gcd;
		x=(x%ty+ty)%ty;
		ans=ans+x*mul;
		mul=b[i]/exgcd(b[i],mul,x,y)*mul;
	}

NOI2018 屠龙勇士

继续拓展

\begin{Bmatrix} b_{1}*x\equiv a_{1}(\mod m_{1})\\b_{2}* x\equiv a_{2}(\mod m_{2}) \\ b_{3}*x\equiv a_3(\mod m_{3}) \end{Bmatrix}

继续构造解,对于  x\equiv b_i*a_i(\mod m_{i}) , 令lcm_1=(m_1,m_2,...,m_i), lcm=\frac{lcm1}{gcd(lcm_1,m_1),gcd(lcm_2,m_2)...,gcd(lcm_i,m_i)},ans为前i-1组方程的解

那么需要找到一个x使  b_i*(ans+x*lcm)\equiv a_{i}(\mod m_{i}) ,那么ans+x*lcm一定是前i组方程的通解

因为 b_i*(b_k*ans)\equiv a_k(\mod m_k),lcm*x*b_i*b_k=0(\mod m_i),k\in[1,i-1]

求出x,并且使ans=ans+x*lcm

其他部分实现都很简单吧

#include<bits/stdc++.h>
using namespace std;
typedef __int128 ll;
ll read();
void write(ll x);
int t;
ll a[100010],b[100010],p[100010],jl[100010],n,m;
ll x,y,ans,mul;
void excrt();
ll exgcd(ll a,ll b,ll &x,ll &y);
int main(){
	scanf("%d",&t);
	while(t--){
		ans=0;
		mul=1;
		n=read(),m=read();
		for(int i=1;i<=n;i++) a[i]=read();
		for(int i=1;i<=n;i++) p[i]=read();
		for(int i=1;i<=n;i++) jl[i]=read();
		for(int i=1;i<=m;i++) b[i]=read();
		if(p[1]==1){
			multiset<int>s;
			s.clear();
			for(int i=1;i<=m;i++) s.insert(b[i]);
			for(int i=1;i<=n;i++){
				multiset<int>::iterator it=s.upper_bound(a[i]);
				it==s.begin()?it:it--;
				s.erase(it);
				ll bnow=(*it); 
				ans=max(ans,(ll)ceil((double)a[i]/bnow));
				s.insert(jl[i]);
			}
			write(ans);
		}
		else{
			excrt();
			if(ans!=-1) write(ans);
			else printf("-1");
		} 
	 	putchar('\n');
	}
	return 0;
}
void excrt(){
	multiset<ll>s;
	s.clear();
	for(int i=1;i<=m;i++) s.insert(b[i]);
	for(int i=1;i<=n;i++){
		multiset<ll>::iterator it=s.upper_bound(a[i]);
		if(it!=s.begin()) it--;
		s.erase(it);
		ll bnow=(*it);
 		ll gcd=exgcd(mul*bnow,p[i],x,y);
		ll d=(a[i]-ans*bnow%p[i]+p[i])%p[i];
		if(d%gcd){
			ans=-1;
			return ;
		}
		x=x*(d/gcd);
		ll ty=p[i]/gcd;
		x=(x%ty+ty)%ty;
		ans+=mul*x;		
		mul=mul*(p[i]/gcd);
		s.insert(jl[i]);
	}
	ans=(ans%mul+mul)%mul;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
	if(!b){
		x=1,y=0;
		return a;
	}
	ll gcd=exgcd(b,a%b,x,y);
	ll ty=x;
	x=y,y=ty-a/b*y;
	return gcd;
}


ll read(){
	ll a=0;
	char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') a=a*10+ch-'0',ch=getchar();
	return a;
}
void write(ll x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10+'0');
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值