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

\(\color{#0066ff}{ 题目描述 }\)

给定 \(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}\)

的最小非负整数解。

\(\color{#0066ff}{输入格式}\)

输入第一行包含整数 \(n\)

接下来 \(n\)行,每行两个非负整数 \(a_i, b_i\)

\(\color{#0066ff}{输出格式}\)

输出一行,为满足条件的最小非负整数 \(x\)

\(\color{#0066ff}{输入样例}\)

3
11 6
25 9
33 17

\(\color{#0066ff}{输出样例}\)

809

\(\color{#0066ff}{数据范围与提示}\)

\(n≤10^5,1≤ai≤10^{12},0≤bi≤10^{12},bi<ai\),保证答案不超过 \(10^{18}\)

请注意程序运行过程中进行乘法运算时结果可能有溢出的风险。

数据保证有解

\(\color{#0066ff}{ 题解 }\)

考虑已经求出前k-1个方程的解,即当前的x满足前k-1个方程,考虑第k个

发现x加减N(前k-1个方程的\(a_i\)的lcm)的整数倍,这个x依然满足前k-1个方程

因此我们要找到t,使得\(x+t*N\equiv b_i\mod a_i\)

移项,\(t*N\equiv b_i-x\mod a_i\)

因此\(k*a_i+t*N=b_i-x\)

这可以扩欧求,求出最小的非负整数解,更新ans就行了

#include<bits/stdc++.h>
#define LL long long
LL in() {
	char ch; LL x = 0, f = 1;
	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
	return x * f;
}
const int maxn = 1e5 + 10;
LL A[maxn], B[maxn], n;
LL msc(LL x, LL y, LL z) {
	LL re = 0;
	while(y) {
		if(y & 1) re = (re + x) % z;
		x = (x + x) % z;
		y >>= 1;
	}
	return re;
}
LL exgcd(LL a, LL b, LL &x, LL &y) {
	if(!b) return x = 1, y = 0, a;
	LL r = exgcd(b, a % b, x, y);
	LL t = x - a / b * y;
	return x = y, y = t, r;
}
LL excrt() {
	LL ans = B[1], N = A[1];
	for(int i = 2; i <= n; i++) {
		LL x, y, c = ((B[i] - ans) % A[i] + A[i]) % A[i];
		LL gcd = exgcd(A[i], N, x, y);
		if(c % gcd) return 233;
		y = msc(y, c / gcd, A[i] / gcd);
		ans += y * N;
		N *= A[i] / gcd;
		((ans %= N) += N) %= N;
	}
	return ans;
}
int main() {
	n = in();
	for(int i = 1; i <= n; i++) A[i] = in(), B[i] = in();
	printf("%lld\n", excrt());
	return 0;
}
posted @ 2019-01-26 15:47  olinr  阅读(194)  评论(0编辑  收藏  举报