P4777 扩展中国剩余定理(exCRT)

P4777 扩展中国剩余定理

简要题意

给定 n n n组非负整数 a i , b i a_i, b_i ai,bi,求解关于 x x x的方程组的最小非负整数解.

每个方程的形式如下:

x ≡ a i ( m o d    b i ) . x\equiv a_i (mod \:\:b_i). xai(modbi).

其中 b 1 , b 2 , b 3 . . . b k b_1,b_2,b_3...b_k b1,b2,b3...bk为不一定两两互质的整数.

拓展中国剩余定理

我们使用数学归纳的方法,假设已经求出前 k − 1 k-1 k1个方程组成的同余方程组的一个解为 a n s ans ans ,且有 N = l c m ( b 1 , b 2 , . . . , b k − 1 ) N=lcm(b_1,b_2,...,b_{k-1}) N=lcm(b1,b2,...,bk1)

则前 k − 1 k-1 k1个方程的方程组通解为 a n s + i ∗ N ( i ∈ Z ) ans+i*N(i\in Z) ans+iN(iZ) .

那么对于加入第 k k k个方程后的方程组,

我们就是要求一个正整数 t t t,使得 a n s + t ∗ N ≡ a k ( m o d    b k ) ans+t*N \equiv a_k(\mod b_k) ans+tNak(modbk)

转化一下上述式子得 t ∗ N ≡ a k − a n s ( m o d    b k ) t*N \equiv a_k-ans(\mod b_k) tNakans(modbk).

对于这个式子我们已经可以通过扩展欧几里得求解 t t t.

若该同余式无解,则整个方程组无解, 若有,即方程满足 g c d ( a , b ) ∣ a k − a n s gcd(a,b)∣a_k-ans gcd(a,b)akans ,则前 k k k个同余式组成的方程组的一个解为 x k = x + t ∗ M x_k=x+t*M xk=x+tM

警告:接下来的步骤可能有些难理解,请动笔跟着思路走.


往后计算取模时就取通解间隔的模了.

(感谢@千年之狐_天才 大佬的帮助)

所以整个算法的思路就是求解k次扩展欧几里得.

程序实现

#include<bits/stdc++.h>
#define ll long long 
#define maxn 100010
using namespace std;
int n;
ll a[maxn],b[maxn];
ll xi,yi;
ll fast_mul(ll ai,ll bi,ll mod){
	ll ret=0;
	while(bi){
		if(bi%2)ret=(ret+ai)%mod;
		ai=(ai+ai)%mod;
		bi/=2;
	}
	return ret;
}//快速乘,直接乘再取模会慢而且爆long long
ll exgcd(ll ai,ll bi){
	if(bi==0){
		xi=1,yi=0;
		return ai;
	}
	ll ret=exgcd(bi,ai%bi);
	ll zi=xi;
	xi=yi;
	yi=zi-(ai/bi)*yi;
	return ret;
}//拓展欧几里得顺便求gcd
ll excrt(){
	ll ans=a[1],N=b[1];
	for(int i=2;i<=n;i++){
		ll ai=N,bi=b[i],ci=(a[i]-ans%bi+bi)%bi;
		ll gcd=exgcd(ai,bi),bg=bi/gcd;//此时算出的xi是对于方程ai*xi+bi*yi=gcd(a,b)的一个可行解,所以接下来还要处理
		if(ci%gcd!=0){printf("No answer.");exit(0);}
		xi=fast_mul(xi,ci/gcd,bg);
		ans+=xi*N;
		N*=bg;
		ans=(ans%N+N)%N;
	}
	return (ans%N+N)%N;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld%lld",&b[i],&a[i]);//注意由于格式的要求,为了满足一般习惯如此输入
	printf("%lld\n",excrt());
	return 0;
}

u p d &MediumSpace; 8.8 upd\:8.8 upd8.8可以处理余数都为0的情况.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值