HDU 1788-中国剩余定理

题意:给一组模线性方程组

x ≡ a1 (mod m1)

x ≡ a2 (mod m2)

......

x ≡ ai (mod mi)

求满足条件的最小的x。。。

思路:明显是中国剩余定理。。不过m1, m2, m3, ... mi 没说一定互质,所以就要将方程组两两合并得到最后结果。wa了两次,在合并的过程中可能会爆int,要用longlong,又被坑了。。。

合并过程:

由 x ≡ a1 (mod m1) 可得 x = a1 + m1 * k1①   合并方程组①,②可得 a1 + m1 * k1 = a2 + m2 * k2;

     x ≡ a2 (mod m2)         x = a2 + m2 * k2②   整理得  m1 * k1 + m2 * (-k2) = a2 - a1;

令a = m1, b = m2, x = k1, y = -k2, c = a2 - c1, g = gcd(a, b);   即求 a * x + b * y = c 的解

利用扩展欧几里得可求得x, y 的一组解x0, y0, 通解为(x = x0 + b / g, y = y0 - a / g)

求出k1 = (x0 % b / g +b / g) % (b / g), 将其代入①式,解出x

于是方程组合并为 y x (mod lcm(m1, m2)), 继续重复这一步骤,直到所有的方程组合并为一个方程,最后的y就是要求的答案



代码:

# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 10 + 5;
int m[maxn], a[maxn];
int n, k;

ll exgcd(ll a, ll b, ll& x, ll& y) {
    if (b == 0) {
        x = 1; y = 0; return a;
    }
    ll g = exgcd(b, a % b, x, y);
    ll t = x; x = y; y = t - a / b * y;
    return g;
}

void Merge(ll&, ll&, ll, ll);

int main(void)
{
    while (scanf("%d %d", &n, &k) && (n || k)) {
        for (int i = 0; i < n; ++i) scanf("%d", m + i);
        for (int i = 0; i < n; ++i) a[i] = m[i] - k;
        ll m0 = m[0], a0 = a[0];
        for (int i = 1; i < n; ++i) {
            Merge(m0, a0, m[i], a[i]);
        }
        printf("%lld\n", (a0 % m0+ m0) % m0);
    }

    return 0;
}

void Merge(ll& m1, ll& a1, ll m2, ll a2) {
    ll k1, k2, c = a2 - a1;
    ll g = exgcd(m1, m2, k1, k2);
    k1 = c / g * k1;
    k1 = (k1 % (m2 / g) + (m2 / g)) % (m2 / g);
    ll x = k1 * m1 + a1;
    m1 = m1 / g * m2;
    a1 = (x % m1 + m1) % m1;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值