poj2891 Strange Way to Express Integers poj1006 Biorhythms 同余方程组

怎样求同余方程组?如:

\[\begin{cases} x \equiv a_1 \pmod {m_1} \\ x \equiv a_2 \pmod {m_2} \\ \cdots \\ x \equiv a_n \pmod {m_n} \end{cases}\]

不保证 \(m\) 两两互素?
两两合并!
比方说

\[\begin{cases} x \equiv a_1 \pmod {m_1} \\ x \equiv a_2 \pmod {m_2} \\ \end{cases}\]

就是

\[\begin{cases} x = m_1x_1+a_1\\ x = m_2x_2+a_2\\ \end{cases}\]

可以变形成

\[m_1x_1+m_2(-x_2)=a_2-a_1 \]

拿扩欧搞掉这个方程。我们肯定想让 \(x\) 最小,那就让 \(x_1\) 最小,这样就求出了 \(x\) 的特解 \(x'\)
显然, \(x\) 的通解是 \(x=x'+[m_1,m_2] \times t ,t \in \mathbb{Z}\)
这也就很像是

\[x \equiv x' \pmod {[m_1,m_2]} \]

我们惊喜地发现两个方程变成了一个方程。一路做下去就好了。

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
int n;
ll a, r, m, aa, rr, x, y;
bool flag;
ll exgcd(ll a, ll b, ll &x, ll &y){
	if(!b){
		x = 1;
		y = 0;
		return a;
	}
	ll re=exgcd(b, a%b, x, y);
	ll z=x;
	x = y;
	y = z - a / b * y;
	return re;
}
int main(){
	while(scanf("%d", &n)!=EOF){
		flag = true;
		n--;
		scanf("%lld %lld", &aa, &rr);
		while(n--){
			scanf("%lld %lld", &a, &r);
			if(!flag)	continue;
			ll gcd=exgcd(aa, a, x, y);
			if((r-rr)%gcd)	flag = false;
			else
				x = (((r-rr)/gcd*x)%(a/gcd)+a/gcd)%(a/gcd);
			x = rr + x * aa;
			rr = x;
			aa = a/gcd*aa;
		}
		if(flag)	printf("%lld\n", x);
		else	printf("-1\n");
	}
	return 0;
}

如果保证两两互素呢?那就中国剩余定理了。记 \(m =\prod_{i=1}^n m_i\)\(M_i=m/m_i\)\(t_i\)\(M_i\) 在模 \(m_i\) 意义下的乘法逆元,则一个特解是 \(\sum_{i=1}^n a_iM_it_i\)。通解是 \(\sum_{i=1}^n a_iM_it_i + mk, k \in \mathbb{Z}\)
证明:因为当 \(i \not =j\)时,\(m_j|M_i\),则 \(a_iM_it_i \equiv 0 \pmod {m_j}\),而 \(a_jM_jt_j \equiv a_j \pmod {m_j}\),证毕。

#include <iostream>
#include <cstdio>
using namespace std;
int a[15], cnt, mul, ans, x, y, dd;
const int m[]={0, 23, 28, 33};
int exgcd(int aa, int bb, int &x, int &y){
	if(!bb){
		x = 1;
		y = 0;
		return aa;
	}
	int re=exgcd(bb, aa%bb, x, y);
	int z=x;
	x = y;
	y = z - aa / bb * y;
	return re;
}
int ni(int aa, int bb){
	int gcd=exgcd(aa, bb, x, y);
	return (x%bb+bb)%bb;
}
int main(){
	while(scanf("%d %d %d %d", &a[1], &a[2], &a[3], &dd)!=EOF){
		mul = 1;
		if(a[1]<0)	break;
		ans = 0;
		for(int i=1; i<=3; i++)
			a[i] %= m[i], mul *= m[i];
		for(int i=1; i<=3; i++)
			ans += a[i] * (mul/m[i])%mul * ni(mul/m[i], m[i])%mul;
		ans -= dd;
		ans = (ans%mul+mul)%mul;
		if(!ans)	ans += mul;
		printf("Case %d: the next triple peak occurs in %d days.\n", ++cnt, ans);
	}
	return 0;
}
posted @ 2018-03-05 16:56  poorpool  阅读(154)  评论(0编辑  收藏  举报