POJ2891 Strange Way to Express Integers

传送门

题目大意:求解同余方程组,不保证模数两两互质。

如果模数互质的话,那么我们可以用CRT求解,要是不互质呢?我们就没办法了吗?当然不是。我们可以用exgcd将方程合并来求解。

首先我们先以两个方程为例:

A ≡ r1 (mod a1)

A ≡ r2 (mod a2)

我们把这两个方程写成这样的形式:A = r1 - x * a1,A = r2 + y * a2.

两式相减一下,得到:x * a1 + y * a2 = r1 -  r2.

这就是一个不定方程的形式。它有解的条件是 (a1,a2) | r1 - r2,否则这个方程无解,那么整个同余方程组也无解。

如果有解的话,那么我们可以先用exgcd求出不定方程的特解x,把它乘以(r1-r2)/(a1,a2),这样的话我们就得到了最小正整数解x,把它带回到第一个同余方程里,那么我们就知道了一个A的特解A0。

之后,又因为A的解必然与A0关于[a1,a2]同余(这个是定理),那么我们就得到了一个新的同余方程:A≡A0 (mod [a1,a2])

我们就把两个方程合并为一个方程,之后继续合并即可得解,注意合并过程中任意一次出现无解的情况那么整个方程即无解。

看一下代码。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<queue>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')

using namespace std;
typedef long long ll;
const int M = 1000005;

int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') op = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
    }
    return ans * op;
}

ll n,r[M],a[M],x,y;

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

ll solve()
{
    ll M = a[1],R = r[1];
    rep(i,2,n)
    {
        ll d = exgcd(M,a[i],x,y);
        if((R - r[i]) % d) return -1;
        x = (R - r[i]) / d * x % a[i];//x是本次解得的一组特解
        R -= x * M;//R是现在的余数,也就是A0
        M = M / d * a[i];//M是新的模数,也就是[a1,a2] 
        R %= M;
    }
    R = (R+M) % M;
    return R;
}

int main()
{
    while(scanf("%lld",&n) != EOF)
    {
        rep(i,1,n) a[i] = read(),r[i] = read();
        printf("%lld\n",solve());
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/captain1/p/9783918.html