中国剩余定理模板

中国剩余定理模板

要解决中国剩余定理的问题,分以下几个步骤:

  1. 会写扩展欧几里得算法
  2. 会求逆元
  3. 线性累加

例题

P1495 【模板】中国剩余定理(CRT)/曹冲养猪

代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

ll arr[15];
ll with[15];

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

ll inv(ll a, ll n)
{
    ll x, y;
    exgcd(a, n, x, y);

    return x;
}

int main()
{
    int N;
    cin >> N;
    ll acc = 1;
    for (int i = 0; i < N; i++)
    {
        cin >> arr[i] >> with[i];
        acc *= arr[i];
    }
    ll np = acc;
    ll ans = 0;
    for (int i = 0; i < N; i++)
    {
        acc /= arr[i];

        ll iv = inv(acc, arr[i]);

        ans = (acc * iv * with[i] + ans) % np;

        acc *= arr[i];
    }
    while (ans < 0)
        ans += np;
    cout << ans;

    return 0;
}

应用

对于模运算,如果底数比较大,我们可以通过CRT进行分解合并。

那么:

( a + b )   m o d   n ↔ ( ( a 1 + b 1 )   m o d   n , ( a 2 + b 2 )   m o d   n , … , ( a k + b k )   m o d   n ) ( a − b )   m o d   n ↔ ( ( a 1 − b 1 )   m o d   n , ( a 2 − b 2 )   m o d   n , … , ( a k − b k )   m o d   n ) ( a b )   m o d   n ↔ ( ( a 1 b 1 )   m o d   n , ( a 2 b 2 )   m o d   n , … , ( a k b k )   m o d   n ) (a+b) \ mod \ n \leftrightarrow ((a_{1}+b_{1}) \ mod \ n,(a_{2}+b_{2}) \ mod \ n,\dots,(a_{k}+b_{k}) \ mod \ n) \\ (a-b) \ mod \ n \leftrightarrow ((a_{1}-b_{1}) \ mod \ n,(a_{2}-b_{2}) \ mod \ n,\dots,(a_{k}-b_{k}) \ mod \ n) \\ (ab) \ mod \ n \leftrightarrow ((a_{1}b_{1}) \ mod \ n,(a_{2}b_{2}) \ mod \ n,\dots,(a_{k}b_{k}) \ mod \ n) \\ (a+b) mod n((a1+b1) mod n,(a2+b2) mod n,,(ak+bk) mod n)(ab) mod n((a1b1) mod n,(a2b2) mod n,,(akbk) mod n)(ab) mod n((a1b1) mod n,(a2b2) mod n,,(akbk) mod n)

扩展中国剩余定理(EXCRT)

上述中国剩余定理的解,我们是通过构造解的形式来进行求解,那么当模不互质的时候我们又该怎么办呢?

扩展中国剩余定理又称同余线性方程组通解定理。

我们有一下同余方程组:

x ≡ a 1 m o d    m 1 x ≡ a 2 m o d    m 2 ⋯ x ≡ a n m o d    m n x \equiv a_1 \mod m_1 \\ x \equiv a_2 \mod m_2 \\ \cdots \\ x \equiv a_n \mod m_n xa1modm1xa2modm2xanmodmn

我们考虑第一个方程的通解为 x 1 = a 1 + t m 1 x_1=a_1+tm_1 x1=a1+tm1,那么我们把它带入到第二个方程中,得到:

a 1 + t m 1 ≡ a 2 m o d    m 2 t m 1 ≡ a 2 − a 1 m o d    m 2 a_1+tm_1 \equiv a_2 \mod m_2 \\ tm_1 \equiv a_2 - a_1 \mod m_2 a1+tm1a2modm2tm1a2a1modm2

这是一个线性同余方程,可以使用扩展欧几里得定理求解,假设通解为 t = x 0 + k i t=x_0+ki t=x0+ki,其中 x 0 x_0 x0为特解, k k k为模系数, i i i为迭代变量,我们回代 t t t中,得到: x 2 = a 1 + m 1 x 0 + m 1 k i x_2=a_1+m_1x_0+m_1ki x2=a1+m1x0+m1ki,我们得到了一个变量为 i i i的新的通解,继续带入第三个方程,依次类推,最终我们得到 x n = a + b i x_n=a+bi xn=a+bi的形式,此时的解就是我们想要的解了。

若迭代求解中,其中一个同余方程无解,那么整个同余方程组无解。

P4777

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

#define FR freopen("in.txt", "r", stdin)
#define FW freopen("out.txt", "w", stdout)

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

ll mul(ll a, ll b, ll p)
{
    ll ans = 0;
    a = (a % p + p) % p;
    b = (b % p + p) % p;
    for (; b; b >>= 1, a = (a + a) % p)
    {
        if (b & 1)
            ans = (ans + a) % p;
    }
    return ans;
}

int main()
{
    int N;
    scanf("%d", &N);
    ll m = 0, n = 1;
    while (N--)
    {
        ll ai, bi;
        scanf("%lld %lld", &ai, &bi);
        bi = (bi - m) % ai;
        ll x, y;
        ll d = exgcd(n, ai, x, y);
        ll wi = ai / d;
        ll x0 = mul(x, bi / d, wi);
        ll t = n;
        n *= wi;
        m = (mul(t, x0, n) + m) % n;
    }
    m = ((m % n) + n) % n;
    printf("%lld", m);
    return 0;
}

P4774 屠龙勇士

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

#define FR freopen("in.txt", "r", stdin)

typedef long long ll;
int n, m;
ll ai[100005];
ll pi[100005];
ll qi[100005];

ll ti[100005];
ll ui[100005];

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

ll mul(ll a, ll b, ll p)
{
    a = (a % p + p) % p;
    b = (b % p + p) % p;
    ll ans = 0;
    for (; b; b >>= 1, a = (a + a) % p)
    {
        if (b & 1)
            ans = (ans + a) % p;
    }
    return ans;
}

void excrt()
{
    // solve
    ll eff = 1;
    ll cst = 0;
    ll mod = pi[1];
    for (int i = 1; i <= n; i++)
    {
        ll p = pi[i];
        ll a = (ui[i] * eff) % p;
        ll b = (ai[i] - (ui[i] * cst) % p) % p;
        ll x, y;
        ll d = exgcd(a, p, x, y);

        if (b % d != 0)
        {
            printf("-1\n");
            return;
        }
        ll Eff = p / d;
        ll ef = eff * Eff;
        ll Cst = mul(x, b / d, ef);

        // ll Cst = (x * (b / d)) % ef;
        cst = cst + mul(eff, Cst, ef) % ef;
        eff = ef;
    }

    ll ks = ((cst % eff) + eff) % eff;

    for (int i = 1; i <= n; i++)
    {
        while (ai[i] > ks * ui[i])
            ks += eff;
    }

    printf("%lld\n", ks);
}
int T;
multiset<ll> lis;
void solve()
{
    //! input
    lis.clear();
    scanf("%d %d", &n, &m);

    for (int i = 1; i <= n; i++)
    {
        scanf("%lld", ai + i);
    }

    for (int i = 1; i <= n; i++)
    {
        scanf("%lld", pi + i);
    }

    for (int i = 1; i <= n; i++)
    {
        scanf("%lld", qi + i);
    }

    for (int i = 1; i <= m; i++)
    {
        scanf("%lld", ti + i);
    }

    //! match

    for (int i = 1; i <= m; i++)
    {
        lis.insert(ti[i]);
    }

    for (int i = 1; i <= n; i++)
    {
        auto it = lis.upper_bound(ai[i]);
        if (it != lis.begin())
        {
            it--;
        }
        ui[i] = *it;
        lis.erase(it);
        lis.insert(qi[i]);
    }

    //! excrt
    excrt();
}

int main()
{
    scanf("%d", &T);
    while (T--)
    {
        solve();
    }
    return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值