【洛谷P4777】扩展中国剩余定理(EXCRT)

题目

题目链接:https://www.luogu.com.cn/problem/P4777
给定 \(n\) 组非负整数 \(a_i, b_i\) ,求解关于 \(x\) 的方程组的最小非负整数解。

\[\begin{cases} x \equiv b_1\ ({\rm mod}\ a_1) \\ x\equiv b_2\ ({\rm mod}\ a_2) \\ ... \\ x \equiv b_n\ ({\rm mod}\ a_n)\end{cases} \]

思路

\(Excrt\)模板。顺便A了crt模板
假设我们已经求出了前\(i-1\)个方程的解\(x\),设\(M=\text{LCM}^{i-1}_{j=1}\ p[j]\),那么显然我们需要找到一个数\(t\)满足\(x+tM\equiv a_i(\rm mod\ p_i)\),也就是说\(x+tM=a_i-y·p_i\)
移项得\(t·M+y·p_i=a_i-x\),其中\(M,x,a_i,p_i\)都是已知项。
那么就可以用\(Exgcd\)求出\(t\)\(y\)\(d=gcd(M,p_i)\)。由于数据保证有解,那么一定有\(d|(a_i-x)\),所以让\(x=x\times \frac{((a_i-x)\rm mod\ p+p)\rm mod\ p}{d}\)即可。注意这一步可能会超出\(\rm long\ \rm long\)范围,所以用龟速乘即可。
那么前\(i\)个同余方程组的解即为\(x+tM\)
然后更新\(x,M\)即可。
总的来说就是做\(n-1\)\(Exgcd\)

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N=100010;
ll ans,M,a,p;
int n;

ll qmul(ll a,ll b,ll MOD)
{
	ll ans=0;
	for (;b;b>>=1,a=(a+a)%MOD)
		if (b&1) ans=(ans+a)%MOD;
	return ans;
}

ll exgcd(ll a,ll b,ll &x,ll &y)
{
	if (!b) 
	{
		x=1; y=0;
		return a;
	}
	ll d=exgcd(b,a%b,x,y);
	ll temp=x; x=y; y=temp-y*(a/b);
	return d;
}

void excrt()
{
	ll x=0,y=0;
	ll Gcd=exgcd(M,p,x,y);
	x=qmul(x,((a-ans)%p+p)%p/Gcd,p);
	ans+=x*M;
	M*=p/Gcd;
	ans=(ans%M+M)%M;
}

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%lld%lld",&p,&a);
		if (i==1) M=p,ans=a;
			else excrt();
	}
	printf("%lld",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/12230455.html