POJ_2891_中国剩余定理

来源:互联网 发布:mysql union all 性能 编辑:程序博客网 时间:2024/06/05 14:56

Description

Elina is reading a book written by Rujia Liu, which introduces a strange way to express non-negative integers. The way is described as following:
Choose k different positive integers a1, a2, …, ak. For some non-negative m, divide it by every ai (1 ≤ i ≤ k) to find the remainder ri. If a1, a2, …, ak are properly chosen, m can be determined, then the pairs (ai, ri) can be used to express m.
“It is easy to calculate the pairs from m, ” said Elina. “But how can I find m from the pairs?”
Since Elina is new to programming, this problem is too difficult for her. Can you help her?

Input

The input contains multiple test cases. Each test cases consists of some lines.
Line 1: Contains the integer k.
Lines 2 ~ k + 1: Each contains a pair of integers ai, ri (1 ≤ i ≤ k).

Output

Output the non-negative integer m on a separate line for each test case. If there are multiple possible values, output the smallest one. If there are no possible values, output -1.

Sample Input

2
8 7
11 9

Sample Output

31

解题思路

中国剩余定理:
能求解什么问题呢?
问题:
一堆物品
3个3个分剩2个
5个5个分剩3个
7个7个分剩2个
问这个物品有多少个?
模线性方程组
设总数为n,则n%3=2,n%5=3,n%7=2;

m%a1=r1;
m%a2=r2;
m%a3=r3;

m%an=rn;
所以
m=k1*a1+r1;
m=k2*a2+r2;
=>k1*a1+r1=k2*a2+r2;
=>k1*a1+k2*a2=r2-r1;
=>a1*x+a2*y=c;(扩展欧几里得)
先通过c%gcd(a1,a2)看是否存在解,若存在解,求出x,可得到m=a1*x+r1;解出m。
将m作为一个特解,可得一个解系:M=m+k*lcm(a1,a2)(k为任意整数);
lcm(a,b)是a,b最小公倍数,lcm(a,b)=a*b/gcd(a,b);
变形得到M%lcm(a1,a2)=m;得到一个新的模方程,令A=lcm(a1,a2),R=m;
M%A=R(m)
所以m%a1=r1;m%a2=r2;可以合并成M%lcm(a1,a2)=m;
这样每两个合并,重复操作即可得到最终结果。
代码中
r1=x*a1+r1;
a1=a1*a2/d;
即为M%lcm(a1,a2)=m;
所以r1=m;
a1=lcm(a1,a2)=a1*a2/gcd(a1,a2);

代码

#include <iostream>#include <cstdio>using namespace std;long long gcd(long long a,long long b,long long& x,long long& y )//扩展欧几里得{    if(b==0)    {        x=1;        y=0;        return a;    }    else    {        long long d=gcd(b,a%b,y,x);        y-=x*(a/b);        return d;    }}int main(){    int n;    long long a1,a2,r1,r2,solve;    long long c;    long long x,y;    //两个数据计算出一个n,两次求得的结果与下一次输入的合并,    while(scanf("%d",&n)!=EOF)    {        solve=0;        scanf("%lld%lld",&a1,&r1);        for(int i=1;i<n;i++)        {            scanf("%lld%lld",&a2,&r2);            c=r2-r1;            long long d=gcd(a1,a2,x,y);            if(c%d!=0)                solve=1;            x=x*(c/d);            x=(x%(a2/d)+(a2/d))%(a2/d);//最小整数解            r1=x*a1+r1;//更新r1,两个数据求得的m            a1=a1*a2/d;//更新a1        }        if(solve==1)            printf("-1\n");        else            printf("%lld\n",r1);    }    return 0;}
原创粉丝点击