luogu4777[模板]拓展中国剩余定理题解

题目链接

https://www.luogu.org/problemnew/show/P4777

分析

扩展\(CRT\)就是解决模数不互质的情况,说是扩展\(CRT\),其实都是扩欧...

先来考虑两个方程的情况:\(x \equiv a \mod b\)\(x \equiv c \mod d\)

由方程1得\(x=tb+a\),代入方程2中得\(tb+a \equiv c \mod d\)

把它变得更像方程:\(t \times b+t' \times d = c-a\)

解得\(t'\)后回代即可

那么对于多个方程组,假设对于前\(k\)个方程组我们已经求出一个解\(x\),记\(M= \prod_{i=1}^k m_i\),那么显然\(x+i \times M\)是前\(k\)个方程的一个通解,因为\(M \equiv 0 \mod m_i (i<=k)\)

那么我们要求的就是一个整数\(t\),使得\(x+ t \times M \equiv b _ {k+1} \mod m _ {k+1}\)

移项得\(t \times M + t' \times m _ {k+1} = b _ {k+1}- x\)(这里的\(x\)其实是已知的)

运用扩欧算出\(t\),更新\(x=x+t \times M\),然后\(M= M \times m _ {k+1}\)

当然无解的情况也就是扩欧无解的情况,当\(b _ {k+1}-x\)不整除\(gcd(M,m _ {k+1})\)时无解

注意

题目要求要将\(x\)化为最小整数解

代码

#include <cstdio>
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#define ll __int128
#define ri register int 
using std::min;
using std::max;
template <class T>inline void read(T &x){
	x=0;int ne=0;char c;
	while(!isdigit(c=getchar()))ne=c=='-';
	x=c-48;
	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
	x=ne?-x:x;return ;
}
const int maxn=100005;
const int inf=0x7fffffff;
ll b[maxn],m[maxn];
int n;
ll exgcd(ll a,ll b,ll &x,ll &y){
	if(b==0){x=1,y=0;return a;}
	ll d=exgcd(b,a%b,x,y);
	ll z=x;x=y,y=z-a/b*y;
	return d;
}
void print(ll x){
    if(!x) return;
    if(x) print(x/10);
    putchar(x%10+'0');
}
ll ans=0;
int main(){
	ll x,y,M,aa,bb,cc,d;
	bool flag=0;
	read(n);
	for(ri i=1;i<=n;i++){
		read(m[i]),read(b[i]);
	}
	M=m[1],ans=b[1];
	for(ri i=2;i<=n;i++){
		aa=M,bb=m[i],cc=(b[i]-ans%bb+bb)%bb;
		x=0,y=0;
		//if(aa<bb) d=exgcd(bb,aa,x,y);
		//else d=exgcd(aa,bb,x,y);
		/*错误点:不要加if不然这样的话方程都改变了,感觉我是真的傻*/
		d=exgcd(aa,bb,x,y);
		bb=bb/d;
		if(cc%d){flag=1;break;}
		x=((x*cc/d)%bb+bb)%bb;
		/*错误点:要先×cc再除d,因为cc保证是d的倍数*/
		ans+=M*x;M*=bb;
		ans=(ans%M+M)%M;
	}
	if(flag)puts("-1");
	else {
		if(!ans)puts("0");
		else {print(ans);puts("");}
		//printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2018-09-14 18:43  Rye_Catcher  阅读(227)  评论(0编辑  收藏  举报