HDU 1573 X问题 (非互质情况下的中国剩余定理)

来源:互联网 发布:淘宝主板为什么要交换 编辑:程序博客网 时间:2024/06/05 14:51

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1573

解题思路:
1. 因为(a1,a2,a3,a4,….,ak)不一定互质,所以不能够直接用中国剩余定理。
2. x=r1+a1*k1,x=r2+a2*k2,所以有r1+a1*k1=r2+a2*k2,化简后得到 a1*k1=(r2-r1) mod(a2);
用扩展欧几里得可以得到最小的k1,所以x=r1+a1*k1+a1*a2/gcd(a1,a2),
就这样一直替换最后剩余一个同余方程。r1就是最后的解。

对于x=a1 mod b1,x= a2 mod b2,设x=a1+m*b1
所以b1*m=a2-a1 mod b2,利用欧几里德扩展定理求出最小的非负m,
那么x=a1+m*b1就已知,且x最小,如果无解,整个同余式组无解
同时,x+k*b1是所有满足x=a1 mod b1的解,而x+k'*b2又是所有满足x=a2 mod b2的解
那么,将x+k*b1与x+k'*b2合并,得到的式子就是x+k*lcm(b1,b2)
于是,上面两个式子可以用x'=x mod lcm(b1,b2)来替代
最后,就只剩下一个式子了,求得的最小的x就是答案
对于一次同余式ax=b mod n,设d=gcd(a,n),则同余式有解的充要条件为d|b
假设d=a*x'+n*y',则x0=b/d*x'一定为方程组的一个解,且共有d个解,
(证明可以参考算法导论)最小正整数解为(x0%n/d+n/d)%(n/d)


#include<cstdio>#include<cstring>#include<cstdlib>#include<cmath>#include<algorithm>using namespace std;#define  N  15#define ll long longvoid ext_gcd(ll a, ll b, ll &d, ll &x, ll &y){       if(!b)       {             x = 1; y = 0; d = a;       }       else       {             ext_gcd(b, a%b, d, y, x);             y -= x * ( a / b);       }}ll  aa[N], rr[N];int  main(){     ll n, m, a, b, c, d, x0, y0;     int ncases, i;     scanf("%d", &ncases);     while(ncases--)     {             scanf("%I64d %I64d", &n, &m);             bool ifhave = 1;             lcm = 1;             for(i = 1; i <= m; i ++)             {                   scanf("%I64d", &aa[i]);             }             for(i = 1;i <= m; i ++)             {                   scanf("%I64d", &rr[i]);             }             for(i = 2; i <= m; i ++)             {                   a = aa[1], b = aa[i], c = rr[i] - rr[1];                   ext_gcd(a, b, d, x0, y0);                   if(c % d)                   {                         ifhave = 0;                         break;                   }                   ll t = b / d;                   x0 = (x0 * (c / d) % t + t) % t;                   rr[1] = aa[1] * x0 + rr[1];                   aa[1] = aa[1] * (aa[i] / d);             }             if(!ifhave)             {                    printf("0\n");                    continue;             }             ll ans = 0;             if(rr[1] <= n)   ans = 1 + (n - rr[1]) / aa[1];             if(ans && rr[1] == 0)  ans --;             printf("%I64d\n", ans);     }     return 0;}


原创粉丝点击