pku2891(很经典,很难的 中国剩余定理)

http://162.105.81.212/JudgeOnline/problem?id=2891

思路:求最小的x,满足 x=ai (mod mi), 这里的ai不互质。考虑两个方程时, x = a1 (mod m1), x = a2 (mod m2), x = a1 + m1*y, x = a2 + m2*z, 联立两式化简得, m1*y+m2*z=a2-a1, 求解出y, 则x=a1+m1*y.对于多个方程,可以另a1=x, m1 = m1*m2, 依次求解下去即可。看了解题报告才能乱七八糟的写了代码:

#include<iostream> using namespace std; __int64 result, d; int flag; __int64 gcd( __int64 a, __int64 b, __int64 &x, __int64 &y ) { __int64 t,ret; if ( ! b ) { x = 1; y = 0 ; return a; } ret = gcd( b, a % b, x, y ); t = x, x = y, y = t - a / b * y; //printf("%I64d/n",ret); return ret; } void fun( __int64 a, __int64 b, __int64 n ) { __int64 e, x, y, i; d = gcd( a, n, x, y ); //printf("%I64d/n",d); if ( b % d != 0 ) flag = 1; result = ( x * (b / d ) % n + n ) % n; } int main() { __int64 a1, m1, a2, m2, t; while(scanf("%I64d",&t) != EOF) { scanf("%I64d%I64d",&m1, &a1); t--; flag = 0; while(t--) { scanf("%I64d%I64d",&m2, &a2); fun(m1, a2-a1, m2); // 很难理解啊、 //printf("%I64d/n",d); //d是m1 和 m2 的最大公约数 a1 = a1 + m1 * result; //对于求多个方程 m1 = (m1 * m2) / d; //lcm(m1,m2)最小公倍数; a1 = (a1 % m1 + m1) % m1; } if(flag) printf("-1/n"); else printf("%I64d/n",a1); } return 0; }  

题意是说,给一组a[i],r[i],求一个最小的m,满足 for_each(i=0: n) a[i]%m=r[i].  

即 a[i]*x+r[i]=m,不存在输出-1。  

我们首先得到 a1 * x + r1 = m 和 a2 * y + r2 = m,联立得到  

a1 * x - a2 * y = r2 - r1,利用ex_gcd解之,先解得a1*X-a2*Y=GCD(a1,a2)时的X Y值和a1,a2的gcd,x y存在整数解的条件是 gcd | r2-r1.是否输出-1就是从这判断。有解即为  

x=(r2-r1)/gcd*X     

tmp=a2/gcd,  x=( ( r2 - r1 ) / gcd * X % tmp + tmp ) % tmp;  

接着输入下一组a3,r3,问题变为求满足LCM(a1,a2)%m1=m和  

a3%m1=r3的新的m值:m1    

LCM(a1,a2) * x + a3 * y = r3 - m  这样只要a1 a2 r1 r2 轮流迭代即可,不用开数组。题目也没说k多大,暗示本题不用开数组。每次更新a1为LCM(a1,a2),r1为m即可。不过注意求x时 ,保持通用性。

你可能感兴趣的:(each,fun)