P4777 【模板】扩展中国剩余定理(EXCRT)

EXCRT模板。我连没EX的CRT都不会打。。。

普通的CRT只能处理那些模互质的,而对于那些不互质的就没办法了。

那么我们引入新的算法来解决这个问题。

先说一下:EXCRT的算法跟CRT区别挺大的。几乎不是同一个算法。


假设我们已经求出了\(z-1\)个方程的解,满足的一个解为\(ans\),当前已经求出的方程模的最小公倍数为\(M\)

满足当前\(z-1\)个方程的通解可以表示为\(ans+kM\)。因为当前部分模的最小公倍数为\(M\)

那么我们需要的是寻找一个解\(t\)使得\(ans+tM \equiv a_i \pmod {m_i}\)

移项得\(tM \equiv a_i - ans \pmod {m_i}\)

同余性质得\(tM-um_i=a_i-ans\)

所以可以用exgcd求解。

只不过我们用exgcd求的是\(xM-ym_i=gcd(M,m_i)\),想要转换过来的话先转成\(t\)

如何转化?使用一个比例式:\(x:t=gcd(M,m_i):(a_i-ans)。\)

转换后的\(t\)是随机的。我们用(%bg+bg)%bg这一招改成最小正整数解。

bg就是exgcd的通解方面的事情了。

最后注意更新一下\(ans,M\),注意\(M\)乘上的是bg。

代码:

#include<cstdio>

const int maxn = 100005;
#define ll long long
ll a[maxn], b[maxn], n;

ll read()
{
    ll ans = 0;
    char ch = getchar();
    while(ch > '9' || ch < '0') ch = getchar();
    while(ch >= '0' && ch <= '9') ans = (ans << 3) + (ans << 1) + ch - '0', ch = getchar();
    return ans;
}
ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b == 0){ x = 1; y = 0; return a; }
    else
    {
        ll ret = exgcd(b, a % b, x, y);
        ll t = x;
        x = y;
        y = t - a / b * y;
        return ret;
    }
}
ll slow_mul(ll x, ll y, ll z)
{
    ll ans = 0, base = x;
    while(y)
    {
        if(y & 1) ans = (ans + base) % z;
        base = (base + base) % z;
        y >>= 1;
    }
    return ans;
}
ll excrt()
{
    ll M = b[1], ans = a[1];
    for(int i = 2; i <= n; i++)
    {
        // ans + k * M
        // ans + t * M === a[i] (mod b[i])
        // t * M === a[i] - ans (mod b[i])
        // t * M - yy * b[i] = a[i] - ans
        // x * M - y * b[i] = gcd(M, b[i])
        // t : x = (a[i] - ans) : gcd(M, b[i])
        // t = x * (a[i] - ans) / gcd(M, b[i])
        
        ll A = M, B = b[i], C = ((a[i] - ans) % b[i] + b[i]) % b[i];
        ll x, y;
        ll g = exgcd(A, B, x, y); ll bg = B / g;
        if(C % g != 0) return -1;
        //x = x * C / g % b[i];
        x = slow_mul(x, C / g, b[i]);
        ans += M * x;
        M *= bg;
        ans = (ans % M + M) % M;
    }
    return (ans % M + M) % M;
}
int main()
{
    n = read();
    for(int i = 1; i <= n; i++) b[i] = read(), a[i] = read();
    printf("%lld\n", excrt());
    return 0;
}
posted @ 2018-10-04 00:49  Garen-Wang  阅读(188)  评论(0编辑  收藏  举报