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

扩展中国剩余定理,是求解形如:
$x\equiv a_{1}($ mod $b_{1})$
$x\equiv a_{2}($ mod $b_{2})$
$x\equiv a_{3}($ mod $b_{3})$
$x\equiv a_{i}($ mod $b_{i})$
...
$x$ 的解(最小非负整数解)

 

考虑只有一个等式,显然,$x$ 的解可以为 $a_{i}$,而$x+t*b_{i}$ 可以表示出 $x$ 的所有解。
考虑我们已经得到一个解 $x$,和一个可以构造出通解的参数 $M$,将该解与下一个方程进行合并,则不难得出等式:$x+y*M=a_{j}+k*b_{j}$,
进行小小的转化,得:
$M*y+b_{j}*(-k)=a_{j}-x$.
这里有一个小问题,就是等式的右边可能是负数,显然这就非常不好办。


好在 $a_{j'}$ 可以由 $a_{j}$ 加上若干个 $b_{j}$ 得到,所以我们将右边进行一下变换:
$(a_{j}-x$ % $b_{j}+b_{j})$ % $b_{j}$.
用 $exgcd$ 求出 $y$ 的值后,可以 $x$ 的新解就是 $x+y*M$.
这时 $M'=M*\frac{b_{j}}{gcd(b_{j},M)}$,至于为什么,由于笔者实力有限,并不知道如何证明这一步。

 

Code:

#include<iostream>
#include<string>
#include<cstdio>
typedef long long ll;
using namespace std;
void setIO(string a){ freopen((a+".in").c_str(),"r",stdin), freopen((a+".out").c_str(),"w",stdout);}
const int maxn=100000+5;
int n;
ll ai[maxn],bi[maxn];
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0){ x=1, y=0; return a;}
    ll gcd=exgcd(b,a%b,x,y);
    ll tp=x;
    x=y, y=tp-a/b*y;
    return gcd;
}
ll mult(ll a,ll b,ll mod){
	long long res=0;
	while(b>0){
		if(b&1) res=(res+a)%mod;
		a=(a+a)%mod;
		b>>=1;
	}
	return res;
}
ll excrt(){
	ll x,y;
	ll ans=bi[1],M=ai[1];
	for(int i=2;i<=n;++i){
		ll a=M,b=ai[i],c=(bi[i]-ans%ai[i]+ai[i])%ai[i];
		ll gcd=exgcd(a,b,x,y);
		x=mult(x,c/gcd,b/gcd);
		ans+=x*M;
		M*=b/gcd;
		ans=(ans%M+M)%M;
	}
	return (ans%M+M)%M;	
}
int main(){
	cin>>n;
	for(int i=1;i<=n;++i) 
		cin>>ai[i]>>bi[i];
	cout<<excrt();
}

  

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值