曹冲养猪

【题目描述】
自从曹冲搞定了大象以后,曹操就开始捉摸让儿子干些事业,于是派他到中原养猪场养猪,可是曹冲满不高兴,于是在工作中马马虎虎,有一次曹操想知道母猪的数量,于是曹冲想狠狠耍曹操一把。举个例子,假如有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 是通式的一个解。

另外,假设  和 都是通式的解,那么:
              
              M∣ - 
 
这里面用到了这么一个定理: 若 c | a, b | a, 那么 lcm(b, c) | a
而在这道题中,mi 互质,所以 (m1 * m2 * …… * mn) |  - ,即 M |  - 
 
所以通式的任意两个解相差 M 的整数倍
那么所有解的形式为
              L[1] * a1 + L[2] * a2 + …… + L[n] * an + k * M 
所以最小整数解就是 mod M 下的解。
 
 
上代码
 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\n", ans);
36 }

 

posted @ 2018-03-03 16:06  mrclr  阅读(325)  评论(0编辑  收藏  举报