中国剩余定理:
国务院:中国油气人均剩余可采存储量仅为世界平均的6%...
咳咳,不对,不是这个
中国剩余定理,又名孙子定理:
听说过韩信点兵吗?
韩信带1500名兵士打仗,战死四五百人,站3人一排,多出2人;站5人一排,多出4人;站7人一排,多出6人。韩信很快说出人数:1049。
韩信一定是读过《孙子算经》的,
书里面有这样一道算术题:“今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何?”
也就是:
一堆物品,
3个3个分剩2个,
5个5个分剩3个,
7个7个分剩2个,
问这个物品有多少个?
解这题,我们需要构造一个答案
我们需要构造这个答案
5*7*inv(5*7, 3) % 3 = 1
3*7*inv(3*7, 5) % 5 = 1
3*5*inv(3*5, 7) % 7 = 1
这3个式子对不对,别告诉我逆元你忘了(*´∇`*),忘了的人请翻阅前几章复习
然后两边同乘你需要的数
2 * 5*7*inv(5*7, 3) % 3 = 2
3 * 3*7*inv(3*7, 5) % 5 = 3
2 * 3*5*inv(3*5, 7) % 7 = 2
令
a = 2 * 5*7*inv(5*7, 3)
b = 3 * 3*7*inv(3*7, 5)
c = 2 * 3*5*inv(3*5, 7)
那么
a % 3 = 2
b % 5 = 3
c % 7 = 2
其实答案就是a+b+c
因为
a%5 = a%7 = 0 因为a是5的倍数,也是7的倍数
b%3 = b%7 = 0 因为b是3的倍数,也是7的倍数
c%3 = c%5 = 0 因为c是3的倍数,也是5的倍数
所以
(a + b + c) % 3 = (a % 3) + (b % 3) + (c % 3) = 2 + 0 + 0 = 2
(a + b + c) % 5 = (a % 5) + (b % 5) + (c % 5) = 0 + 3 + 0 = 3
(a + b + c) % 7 = (a % 7) + (b % 7) + (c % 7) = 0 + 0 + 2 = 2
你看你看,答案是不是a+b+c(。・ω・)ノ゙,完全满足题意
但是答案,不只一个,有无穷个,每相隔105就是一个答案(105 = 3 * 5 * 7)
根据计算,答案等于233,233%105 = 23
如果题目问你最小的那个答案,那就是23了
(233怎么算的呢?
a=2*5*7*2=140;
b=3*3*7*1=63;
c=2*3*5*1=30;
140+63+30=233;
hhh2333333333333333,答案233出来了吧,hhh,不会算逆元的小伙伴该回去复习逆元了!)
以下抄自百度百科
1 #include<cstdio> 2 3 typedef long long LL; 4 5 LL inv(LL t, LL p) {//求t关于p的逆元 6 if(t>=p) 7 t=t%p; 8 return t == 1 ? 1 : (p - p / t) * inv(p % t, p) % p; 9 } 10 11 //n个方程:x=a[i](mod m[i]) (0<=i<n) 12 LL china(int n, LL m[], LL a[]){ 13 LL M = 1, ret = 0; 14 for(int i = 0; i < n; i ++) M *= m[i]; 15 for(int i = 0; i < n; i ++){ 16 LL w = M / m[i]; 17 ret = (ret + w * inv(w, m[i]) * a[i]) % M; 18 } 19 return (ret + M) % M; 20 } 21 int main(){ 22 int n; 23 LL m[2000],a[2000]; 24 scanf("%d",&n); 25 for(int i=0;i<n;i++) 26 { 27 scanf("%d%d",&m[i],&a[i]); 28 } 29 printf("%lld ",china(n,m,a)); 30 }
3个式子:
x%3=2
x%5=3
x%7=2
求x;
运行程序,输入
3
3 2
5 3
7 2
试试看答案是不是23
告诉你一个好消息,上面这个中国剩余定理只是基础,2333333333,是要求m数组里面的元素两两互质的!
那如果不一定是呢?怎么办?
上模板吧:
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 typedef long long LL; 5 typedef pair<LL, LL> PLL; 6 7 LL inv(LL t, LL p) {//求t关于p的逆元 8 if(t>=p) 9 t=t%p; 10 return t == 1 ? 1 : (p - p / t) * inv(p % t, p) % p; 11 } 12 13 PLL linear(LL A[], LL B[], LL M[], int n) {//求解A[i]x = B[i] (mod M[i]),总共n个线性方程组 14 LL x = 0, m = 1; 15 for(int i = 0; i < n; i ++) { 16 LL a = A[i] * m, b = B[i] - A[i]*x, d = __gcd(M[i], a); 17 if(b % d != 0) return PLL(0, -1);//答案不存在,返回-1 18 LL t = b/d * inv(a/d, M[i]/d)%(M[i]/d); 19 x = x + m*t; 20 m *= M[i]/d; 21 } 22 x = (x % m + m ) % m; 23 return PLL(x, m);//返回的x就是答案,m是最后的lcm值 24 } 25 26 int main() 27 { 28 int n; 29 scanf("%d",&n); 30 LL a[2017],b[2017],m[2017]; 31 for(int i=0;i<n;i++) 32 { 33 scanf("%d%d%d",&a[i],&b[i],&m[i]); 34 } 35 PLL pa=linear(a,b,m,n); 36 printf("%lld ",pa.first); 37 }
1*x=2(%3)
1*x=3(%5)
1*x=2(%7)
输入:
3
1 2 3
1 3 5
1 2 7
输出:
23
有兴趣的可以去试试这道题
poj 2891
http://poj.org/problem?id=2891