题意:\(\left\{\begin{aligned}x\equiv\ a_1(\mod b_1) \quad\\ x\equiv\ a_2(\mod b_2) \quad\\ x\equiv\ a_3(\mod b_3) \quad\\ ...\quad\\x\equiv\ a_n(\mod b_n) \quad\end{aligned}\right.\)
其中\(b_1,b_2,b_3...b_n\)不一定两两互质,求x的最小非负整数解.
分析:中国剩余定理中\(b_1,b_2,b_3...b_n\)保证了两两互质,而扩展中国剩余定理中没有,所以它们两个没有半毛钱关系.扩展中国剩余定理只需要用到扩展欧几里得(广告).
假设已经求出前k-1个方程组成的同余方程组的一个解为ans,且有\(M=\prod_{i=1}^{k-1}b_i\)(这里理解为最小公倍数),则前k-1个方程构成的方程组的通解为\(ans+p*M(p\in Z)\)
那么对于前k个方程构成的方程组,我们就是要求一个正整数q,使得\(ans+q*M \equiv a_k(\mod b_k)\).
移项得\(q*M \equiv a_k-ans(\mod b_k)\)
如果你理解了扩展欧几里得算法的话,看到上面这个式子应该会感到很亲切.
我们令\(c=a_k-ans\),则我们要求的就是\(q*M \equiv c(\mod b_k)\),即求出\(gcd=exgcd(M,b_k,x,y)\),如果gcd不是c的约数,则该方程无解,则整个同余方程组无解.
若方程有解,则\(x*(c/gcd)\)就是方程\(q*M \equiv c(\mod b_k)\)的解,则前i个方程构成的方程组的解就是\(ans+[x*(c/gcd)]*M\).
综上我们只要跑k次扩展欧几里得就行了.然后洛咕上这道题相乘的时候会爆long long,要用到龟速乘.
#include<bits/stdc++.h> #define rg register #define LL long long using namespace std; inline LL read(){ rg LL s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();} return s*w; } LL n,a[100005],b[100005]; inline 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,y,x); y-=x*(a/b); return d; } inline LL quickmul(LL a,LL b,LL c){//龟速乘 LL cnt=0; while(b){ if(b&1)cnt=(cnt+a)%c; a=(a+a)%c; b>>=1; } return cnt%c; } inline LL ex_intchina(){//模板 LL ans=a[1],M=b[1]; for(int i=2;i<=n;i++){ LL c=((a[i]-ans)%b[i]+b[i])%b[i]; LL x,y,gcd=exgcd(M,b[i],x,y); if(c%gcd!=0)return -1; x=quickmul(x,c/gcd,b[i]); //当前第i个方程的解 ans+=x*M; //更新前i个方程组的解ans M*=b[i]/gcd; //更新M(所有模数的最小公倍数) ans=(ans%M+M)%M; } return ans; } int main(){ n=read(); for(int i=1;i<=n;i++){ b[i]=read();a[i]=read(); }//转换一下输入,我习惯b[i]是模数 printf("%lld\n",ex_intchina()); return 0; }
来源:https://www.cnblogs.com/PPXppx/p/10547175.html