POJ 2891 Strange Way to Express Integers 中国剩余定理MOD不互质数字方法
http://poj.org/problem?id=2891
7
11323 9793
5537 4754
21538 10901
16118 203
2082 1209
22929 9510
16541 15898
4
18373 16147
8614 14948
8440 17480
22751 21618
6
19576 8109
18992 24177
9667 17726
16743 19533
16358 12524
8280 22731
4
9397 38490
22001 25094
33771 38852
19405 35943
ans
32439
13052907343337200
-1
78383942913636233
题目mod的数字并不互质,那怎么办呢?
记mod[i]表示第i个mod的数字,r[i]表示余数,那么观察前两项
总有X = k1 * mod[1] + r[1];
X = k2 * mod[2] + r[2];
那么k1 * mod[1] - k2 * mod[2] = r[2] - r[1]
这是可以用扩展欧几里德算法求得最小的解k1 (k1可以等于0,不一定要>0,我就是规定满足k1>0后不断溢出,所以wa)
那么得到满足前两个数字的解是(就是满足%mod[1] = r[1] , %mod[2] = r[2])的解。ansx = k1 * mod[1] + r[1]
然后往下合并
怎么往下合并呢?
就是把前面两条等式,变成了 % lcm(mod[1], mod[2]) = rr了,这个rr可以自己解出来,ansx % lcm(mod[1], mod[2])就是了
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; #include <iostream> #include <sstream> #include <vector> #include <set> #include <map> #include <queue> #include <string> const int maxn = 500 + 20; LL mod[maxn]; LL r[maxn]; LL gcd(LL n, LL m) { if (n % m == 0) return m; else return gcd(m, n % m); } LL lcm(LL n, LL m) { return n / gcd(n, m) * m; } LL exgcd (LL a,LL mod,LL &x,LL &y) { //求解a关于mod的逆元 ★:当且仅当a和mod互质才有用 if (mod==0) { x=1; y=0; return a;//保留基本功能,返回最大公约数 } LL g=exgcd(mod,a%mod,x,y); LL t=x; //这里根据mod==0 return回来后, x=y; //x,y是最新的值x2,y2,改变一下,这样赋值就是为了x1=y2 y=t-(a/mod)*y; // y1=x2(变成了t)-[a/mod]y2; return g; //保留基本功能,返回最大公约数 } bool get_min_number (LL a,LL b,LL c,LL &x,LL &y) { //得到a*x+b*y=c的最小整数解 LL abGCD = gcd(a,b); if (c % abGCD != 0) return 0;//不能整除GCD的话,此方程无解 a /= abGCD; b /= abGCD; c /= abGCD; LL tx,ty; exgcd(a,b,tx,ty); //先得到a*x+b*y=1的解,注意这个时候gcd(a,b)=1 x = tx * c; y = ty * c; //同时乘上c,c是约简了的。得到了一组a*x + b*y = c的解。 LL haveSignB = b; if (b < 0) b = -b; //避免mod负数啊 x = (x % b + b) % b; //最小解 // if (x == 0) x = b; //避免x = 0不是"正"整数 不要用这个,溢出 y = (c - a * x) / haveSignB; return 1;//1代表可以 } int n; void work () { for (int i = 1; i <= n; ++i) { scanf("%lld%lld", &mod[i], &r[i]); } LL mm = mod[1]; LL rr = r[1]; LL ansx = 0; for (int i = 2; i <= n; ++i) { LL x, y; int ret = get_min_number(mm, -mod[i], r[i] - rr, x, y); if (ret == 0) { printf("-1\n"); return; } ansx = x * mm + rr; mm = lcm(mm, mod[i]); rr = ansx % mm; } printf("%lld\n", rr); return; } int main() { #ifdef local freopen("data.txt","r",stdin); #endif while(scanf("%d", &n) != EOF && n) { work(); } return 0; }
为什么rr就是最小解呢?余数当然是最小的解啊。。。
posted on 2016-09-30 20:22 stupid_one 阅读(233) 评论(0) 编辑 收藏 举报