POJ 2891 Strange Way to Express Integers

分析来自:oo

题意很简单,给k对(ai,ri),解模线性方程组 m ≡ ri (mod) ai 判定是否有解,求最小解。

有一点比较有问题就是题目的Hint,说输入输出数字均在__int64内。

显然k也包进去了!显然这是不可能的!想像下__int64个方程,O(n)也要超时啊!

所以k还是按int来。

由于k不定,且ai 不互质,因此采用两两合并解的方法。

首先拿前两个方程, m ≡ r1 (mod) a1      m ≡ r2 (mod) a2

两式等同于 m = a1*x1 + r1  m = a2*x2 + r2

前式代入后式,消去m可得 a1*x1 = a2*x2 + r2 - r1

写成模方程,去一个未知数x2, a1*x1  ≡  r2 - r1 (mod) a2 ————@

扩展GCD可判定是否有解,若此方程无解,则后面所有方程都无需判定。

若有解,则能求出所有的解,然后代回方程 m = a1*x1 + r1得到每个可能的 m。 但是

在此,求出所有的解然后向下面每个方程测试显然有些不现实

而能得到的最小解以及这一解系的关系应当能写成相同模方程的形式。

看了很多份解题报告也没理解这个解系的方程是怎么写出来的

每一份解题报告写到这里都只有一句显然就给带过了。

反正我是个弱菜,我是没看出来它是怎么显然的。

@式的最小解x,解系x1 = x + i*(a2 / d)  (d = gcd(a1,a2)  , i = 0~d - 1)  这一点是已知的,在扩展GCD中可求的

然后把x 求出来,得到方程  m  ≡  a1 * x + r1 (mod) ( lcm (a1, a2))

-------------------------

早上爬起来开始翻初等数论书,终于明白差不多了。。T_T

某一句话是这样说的 对方程  ax  ≡  b (mod) n

d = gcd(a,n) 若 d | b 则 有 d 个解 ,最小的解为x0 = (x*(b/d) mod n + n )mod(n/d)

则所有满足方程 x = x0 (mod) (n/d) 的 x 都是原方程的解。

-------------------------

所以在@中求得最小解x后,可知这个方程的所有解可用方程 x1 = x (mod) (a2 / gcd(a1,a2)) 表示

代回m = a1*x1 + r1,得  m = a1 * (x (mod) (a2 / gcd(a1,a2)) ) + r1

移项 (m - r1) / a1 = x % (a2 / gcd(a1,a2)),等同于存在整数 y

y*(a2/ gcd(a1,a2)) + (m - r1)/a1 = x 两边乘 a1有

y * (  a1* a2 /gcd(a1,a2)) + (m - r1) = x*a1

y * (lcm(a1,a2)) + m = x*a1 + r1

写成方程消y,m   ≡   x*a1 + r1 mod (lcm(a1,a2))

 

至此,前两个方程合并为一个方程,取第三个方程与此方程按相同方法联解,至最终只剩一个方程即可。


Code:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <vector>
#include <queue>
#include <cmath>
#include <map>
#include <set>
#define eps 1e-7
#define LL long long
#define pb push_back
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;

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

int main()
{
    LL a1,a2,r1,r2,x,y,k;
    while(scanf("%lld",&k)==1){
        bool flag=false;
        scanf("%lld %lld",&a1,&r1);
        for(LL i=1;i<k;i++){
            scanf("%lld %lld",&a2,&r2);
            if(flag) continue;
            LL g=ex_gcd(a1,a2,x,y);
            LL c=r2-r1;
            if(c%g) flag=true;
            a2/=g;
            r1=((((x*(c/g))%a2)+a2)%a2)*a1+r1;
            a1=a2*a1;
        }
        if(flag) printf("-1\n");
        else printf("%lld\n",r1%a1);
    }
    return 0;
}



你可能感兴趣的:(POJ 2891 Strange Way to Express Integers)