表达整数的奇怪方式AcWing 204 线性同余

题目传送门
在这里插入图片描述
首先看第一个同余方程, x ≡ m 1 ( m o d   a 1 ) x\equiv m_1(mod \ a_1) xm1(mod a1)可以化为 1 × x + a 1 × y = m 1 1\times x+a_1 \times y=m_1 1×x+a1×y=m1
可以根据扩展欧几里得算法求出一个 x x x的可行解,如果是负数

while (x < 0){
    
    
	x = (x + a[1]) % a[1];
}

如果已经有了前k个同余方程的解为 x x x,前k+1个同余方程的解也可以求得

L C M k LCM_k LCMk l c m ( a 1 , a 2 , . . . , a k ) lcm(a_1,a_2,...,a_k) lcm(a1,a2,...,ak)

前k+1个同余方程的解设为 y y y,则一定满足 y = x + L C M k × n y=x+LCM_k\times n y=x+LCMk×n( n n n为整数),

同时 1 × y + a k + 1 × q = m k + 1 1\times y+a_{k+1}\times q=m_{k+1} 1×y+ak+1×q=mk+1

x + L C M k × n + a k + 1 × q = m k + 1 x+LCM_k\times n+a_{k+1}\times q=m_{k+1} x+LCMk×n+ak+1×q=mk+1

L C M k × n + a k + 1 × q = m k + 1 − x LCM_k\times n+a_{k+1}\times q=m_{k+1}-x LCMk×n+ak+1×q=mk+1x

用扩展欧几里得算法检测是否有解,如果该方程无解,那么就不存在答案

若方程有解,则 y = x + L C M k × n y=x+LCM_k\times n y=x+LCMk×n

求解完第一个后,依据上述过程求解后续的解即可

求解过程中,如果扩展欧几里得算法求得了一个负数值,转变为正数,即与模数相加取模即可

最后程序会得到一个值,是一个满足条件的可行解,但是不一定最小,只需要对全部的数字的最小公倍数 L C M n LCM_n LCMn取模即可

假设最小可行解是 a n s ans ans,则通解是 a n s + L C M n × N ans+LCM_n\times N ans+LCMn×N( N N N为整数),通解对每一个 a i a_i ai取模时,可以分为两部分,第一部分取模后显然满足条件,而第二部分的 L C M n × N LCM_n\times N LCMn×N,因为 L C M n LCM_n LCMn a i a_i ai的倍数,因此取模后为0,所以最后结果依然满足条件,因此上述表达式为通解。

#include <bits/stdc++.h>
template <typename T>
void exgcd(T a, T b, T& x, T& y){
    
    
	if (b == 0){
    
    
		x = 1;
		y = 0;
		return;
	}
	exgcd(b, a % b, y, x);
	y -= (a / b) * x;
}
template<typename T>
T gcd(T a, T b){
    
    
return b == 0 ? a : gcd(b, a % b);
}
template<typename T>
T lcm(T a, T b){
    
    
	return a / gcd(a, b) * b;
}
long long a[30], m[30], n;
long long x, y, res, M;
int main(){
    
    
//	freopen("in.in", "r", stdin);
//	freopen("out.out", "w", stdout);
	std::ios::sync_with_stdio(false);
	std::cin >> n;
	for (int i = 1; i <= n; i++){
    
    
		std::cin >> a[i] >> m[i];
	}
	M = a[1];
	exgcd(1ll, a[1], res, y);
	res = (res + a[1]) % a[1];
	res *= m[1] / gcd(1ll, a[1]);
	int i;
	for (i = 2; i <= n; i++){
    
    
		long long temp = m[i] - res;
		while (temp < 0)temp = (temp + a[i]) % a[i];
		temp %= a[i];
		long long _gcd = gcd(M, a[i]);
		if (temp % _gcd){
    
    
			break;
		}
		exgcd(M, a[i], x, y);
		x = (x + a[i]) % a[i];
		x = (x * (temp / _gcd)) % a[i];
		res += (x * M);
		M = lcm(M, a[i]);
	}
	if (i <= n){
    
    
		std::cout << "-1\n";
	}
	else std::cout << res % M << "\n";
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43701790/article/details/109251477
204