您的位置:首页 > 其它

Strange Way to Express Integers(扩展欧几里得+乘法逆元+中国剩余定理求解非互质的模线性方程组)

2015-08-25 14:42 856 查看
Link:http://poj.org/problem?id=2891

Strange Way to Express Integers

Time Limit: 1000MSMemory Limit: 131072K
Total Submissions: 11454Accepted: 3549
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

Hint

All integers in the input and the output are non-negative and can be represented by 64-bit integral types.

Source

POJ Monthly--2006.07.30, Static
题意:给出模线性方程组的每个式子的除数和余数,求最小满足这些式子的被除数是多少。若无解则输出-1。
编程思想:扩展欧几里得+乘法逆元+中国剩余定理。

AC code1(递归版):

/** 
中国剩余定理(不互质) 
*/  
#include <iostream>  
#include <cstdio>  
#include <cstring>  
using namespace std;  
typedef __int64 int64;  
int64 Mod;  
  
int64 gcd(int64 a, int64 b)  
{  
    if(b==0)  
        return a;  
    return gcd(b,a%b);  
}  
  
int64 Extend_Euclid(int64 a, int64 b, int64&x, int64& y)//ax+by=gcd(a,b)  
{  
    if(b==0)  
    {  
        x=1,y=0;  
        return a;  
    }  
    int64 d = Extend_Euclid(b,a%b,x,y);  
    int64 t = x;  
    x = y;  
    y = t - a/b*y;  
    return d;  
}  
  
//a在模n乘法下的逆元,没有则返回-1  
int64 inv(int64 a, int64 n)  
{  
    int64 x,y;  
    int64 t = Extend_Euclid(a,n,x,y);  
    if(t != 1)  
        return -1;  
    return (x%n+n)%n;  
}  
  
//将两个方程合并为一个  
bool merge(int64 a1, int64 n1, int64 a2, int64 n2, int64& a3, int64& n3)  
{  
    int64 d = gcd(n1,n2);  
    int64 c = a2-a1;  
    if(c%d)  
        return false;  
    c = (c%n2+n2)%n2;  
    c /= d;  
    n1 /= d;  
    n2 /= d;  
    c *= inv(n1,n2);  
    c %= n2;  
    c *= n1*d;  
    c += a1;  
    n3 = n1*n2*d;  
    a3 = (c%n3+n3)%n3;  
    return true;  
}  
  
//求模线性方程组x=ai(mod ni),ni可以不互质  
int64 China_Reminder2(int len, int64* a, int64* n)//len为模方程的式子总数,a[i]、n[i]分别表示第i个式子的余数、除数  
{  
    int64 a1=a[0],n1=n[0];  
    int64 a2,n2;  
    for(int i = 1; i < len; i++)  
    {  
        int64 aa,nn;  
        a2 = a[i],n2=n[i];  
        if(!merge(a1,n1,a2,n2,aa,nn))  
            return -1;//无解输出-1  
        a1 = aa;  
        n1 = nn;  
    }  
    Mod = n1;  
    return (a1%n1+n1)%n1;  
}  
int64 a[1000],b[1000];  
int main()  
{  
    int i;  
    int k;  
    while(scanf("%d",&k)!=EOF)  
    {  
        for(i = 0; i < k; i++)  
            scanf("%I64d %I64d",&a[i],&b[i]);  
        printf("%I64d\n",China_Reminder2(k,b,a));  
    }  
    return 0;  
}


AC code2(非递归版):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <string>
#include <queue>
#include <stack>
#include <algorithm>
#define PI acos(-1.0)
#define LINF 1000000000000000000LL
#define eps 1e-8
#define LL long long
#define MAXN 100010 
#define MOD 1000000007
using namespace std;
const int INF=0x3f3f3f3f;
LL Mod;  
LL gcd(LL a, LL b)  
{  
    if(b==0)  
        return a;  
    return gcd(b,a%b);  
}  
LL exgcd(LL A,LL &x,LL B,LL &y)
{
    LL x1,y1,x0,y0;
    x0=1;y0=0;
    x1=0;y1=1;
    LL r=(A%B+B)%B;
    LL q=(A-r)/B;
    x=0;y=1;
    while(r)
    {
        x=x0-q*x1;
        y=y0-q*y1;
        x0=x1;
        y0=y1;
        x1=x;y1=y;
        A=B;B=r;r=A%B;
        q=(A-r)/B;
    }
    return B;
}
LL ABS(LL x)
{
	return x>=0?x:-x;
}
/*接下来介绍乘法逆元:
若m≥1,gcd(a,m)=1,则存在c使得 ca≡1(mod m) 我们把c称为是a对模m的逆,记为 a-1(mod m)或a-1 可以用扩展欧几里德算法求a-1
应用: 求(a/b)%c时,若a为大整数时可以写成 ((a%c)*((b-1)%c))%c,注意:这里(b-1)是b%c的乘法逆元,不是b减 1!!! 
还可以使用扩展欧几里德算法求乘法逆元:*/
//a在模n乘法下的逆元,没有则返回-1(a * b % n == 1,已知a,n,求b就是求a模n的乘法逆元)    
LL inv(LL a, LL n)  
{  
    LL x,y;  
    //int64 t = Extend_Euclid(a,n,x,y); 
	/*******************************************
	扩展欧几里得算法求乘法逆元
	调用:exgcd(a,x,b,y);
	则:  x为a模b的乘法逆元,y为b模a的乘法逆元
	********************************************/ 
    LL t = exgcd(a,x,n,y);  
    if(t != 1)  
        return -1;  
    return (x%n+n)%n;  
}  
  
//将两个方程合并为一个  
bool merge(LL a1, LL n1, LL a2, LL n2, LL& a3, LL& n3)  
{  
    LL d = gcd(n1,n2);  
    LL c = a2-a1;  
    if(c%d)  
        return false;  
    c = (c%n2+n2)%n2;  
    c /= d;  
    n1 /= d;  
    n2 /= d;  
    c *= inv(n1,n2);  
    c %= n2;  
    c *= n1*d;  
    c += a1;  
    n3 = n1*n2*d;  
    a3 = (c%n3+n3)%n3;  
    return true;  
}  
  
//求模线性方程组x=ai(mod ni),ni可以不互质  
LL China_Reminder2(LL len, LL* a, LL* n)//len为模方程的式子总数,a[i]、n[i]分别表示第i个式子的余数、除数  
{  
    LL a1=a[0],n1=n[0];  
    LL a2,n2;  
    for(int i = 1; i < len; i++)  
    {  
        LL aa,nn;  
        a2 = a[i],n2=n[i];  
        if(!merge(a1,n1,a2,n2,aa,nn))  
            return -1;//无解输出-1  
        a1 = aa;  
        n1 = nn;  
    }  
    Mod = n1;  
    return (a1%n1+n1)%n1;  
}  
LL a[1000],b[1000];  
int main()  
{  
    int i;  
    int k;  
    while(scanf("%d",&k)!=EOF)  
    {  
        for(i = 0; i < k; i++)  
            scanf("%I64d %I64d",&a[i],&b[i]);  
        printf("%I64d\n",China_Reminder2(k,b,a));  
    }  
    return 0;  
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: