【题目描述】
自从曹冲搞定了大象以后,曹操就开始捉摸让儿子干些事业,于是派他到中原养猪场养猪,可是曹冲满不高兴,于是在工作中马马虎虎,有一次曹操想知道母猪的数量,于是曹冲想狠狠耍曹操一把。举个例子,假如有16 头母猪,如果建了3 个猪圈,剩下1 头猪就没有地方安家了。如果建造了5 个猪圈,但是仍然有1 头猪没有地方去,然后如果建造了7 个猪圈,还有2 头没有地方去。你作为曹总的私人秘书理所当然要将准确的猪数报给曹总,你该怎么办?
【输入】
第一行包含一个整数n (n <= 10) – 建立猪圈的次数,解下来n 行,每行两个整数ai, bi( bi <= ai <= 1000), 表示建立了ai 个猪圈,有bi 头猪没有去处。你可以假定ai,aj 互质.
【输出】
输出包含一个正整数,即为曹冲至少养母猪的数目。
样例输入
33
1
5 1
7 2
样例输出
16
【分析】
这明显就是一道中国剩余定理的板子题吗!
然而我并不会中国剩余定理,为此现学了一下……
所谓的中国剩余定理,通式就是这个
解法呢,首先我们要构造一个 L[i], L[i] 满足 L[i] % i == 1 && L[i] % j == 0 (j != i)。(i 就是 上面通式中的 a)
构造完这个 L[i] 后,ans = (∑ L[i] * ai (i : 1 -> n) )/ (m₁ * m₂ * …… * mn)。为什么呢,这要慢慢解释。
先具体说一下怎么求 L[i] .
L[i] 需要满足两个条件,我认为其中第二个条件比较好办:只要预先将所有的m都乘起来(设为M),然后除以 i ,这样 M 满足 M% j == 0(j != i) && M % i != 0。此时L[i]已经完成一半了,记录一下为mul[i]。
接下来要使 L[i] % i == 1。一种方法是求逆元。因为 L[i] ≡ 1 (mod i),我们可令 mul[i] * x ≡ 1 (mod i),那么 mul[i] 和 x 互为 mod i 下的逆元。设法求 x 即可。
mul[i] * x ≡ 1 (mod i) 可变形为 mul[i] * x - i * y = 1,这样的话就可以用 exgcd 求 x 了。这样 L[i] 就彻底解决了。
再看看 ans 的求法。
首先对于 L[i] * ai ≡ ai (mod mi) ,而对于任意的 j (j <= n && j != i) L[i] * ai ≡ 0 (mod mj),所以
x = L[1] * a1 + L[2] * a2 + …… + L[n] * an = L[i] *ai + 0 + 0 + …… + 0 (mod mi) = ai (mod mi)。
说明 x 是通式的一个解。
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 using namespace std; 7 typedef long long ll; 8 const int maxn = 15; 9 int n; 10 ll L[maxn], b[maxn], p[maxn], multi[maxn], M = 1; 11 ll d = 1, x, y; 12 ll ans = 0; 13 ll exgcd(ll a, ll b, ll& d, ll& x, ll& y) 14 { 15 if(!b) {d = a; x = 1; y = 0;} 16 else {exgcd(b, a % b, d, y, x); y -= x * (a / b);} 17 } 18 int main() 19 { 20 scanf("%d", &n); 21 for(int i = 1; i <= n; ++i) 22 { 23 scanf("%lld%lld", &p[i], &b[i]); 24 M *= p[i]; 25 } 26 for(int i = 1; i <= n; ++i) 27 { 28 multi[i] = M / p[i]; 29 exgcd(multi[i], p[i], d, x, y); 30 x = (x + p[i]) % p[i]; 31 L[i] = multi[i] * x; 32 ans += (L[i] * b[i]); 33 } 34 ans %= M; 35 printf("%lld ", ans); 36 }