Hdu 3579 Hello Kiki(同餘模方程組)

題意:要求求解出H,其中H%Mi=Ai ; 其中i=1,2,,,,,N;
樣例說明:
T
N
M1,M2,,,,,Mn
A1,A2,,,,,,An
其實該題是要求我們解這樣一個方程組:
這裏寫圖片描述
首先先介紹一下同於方程組的解發;
一:解二元一次非其次方程組
令方程組爲: ax + by = c (1)
看到這式子我們首先要想到的就是歐幾里得擴展原裏,即若c爲a與b的最大公約數,則可以利用歐幾里得擴展原理求出x , y 的解,(至於歐幾里得擴展原理該篇文章不講述),但這裏c不一定是a , b的最大公約數所以要解該方程還需要在原有的歐幾里得擴展原理上修改一下。
我們令d=gcd(a,b),則可以通過歐幾里得擴展原理求出 :
a * x1+b * y1=d (2)
若c%d=0 這(1)方程有解, 因爲在(2)方程兩邊同時乘c/d 就可以得到(1)方程的解了。 所以有 x=x1*(c/d) y=x2*(c/d) 但是這只是該方程的一組解,
接下來求一下該方程的通解
令 (x,y) 是該方程的任意組解。 令由上面的方法我們可以求出方程的一組特解爲(x1,y1),由方程(1)定義可知 :a * x+b * y = c 且 a * x1+b * y1 = c 所以 有 :
a * x+b * y = a * x1+b * y1 ;移項後可得 a * (x-x1)=b * (y1-y) (3) 由於d=gcd(a,b),所以a,b都能整除d ,令A=a/d B=b/d 所以有 A * (x-x1)= B* (y1-y) 且其中A與B互素 所以要想該式成立則 (x-x1) % B=0且(y1-y) % A=0 。所以 x=x1+k * B y=y1-k * A;(其中k爲任意常數)
這裏寫圖片描述

二:解同餘模方程組
這裏寫圖片描述
所以:K1*M1+A1 = K2*M2+A2 移項得到 K1*M1-K2*M2=A2-A1 。
再令a=M1,b=M2,c=(A2-A1) , x=K1,y=-K2 則該表達時可以表示爲a*x+b*y=c, 而a,b,c爲常數要求x,y。這時我們就可以用上面的方法來求解,先通過歐幾里得擴展原理 求出特解(x1,y1) 然後通過通解公式求出: x=x1+k*B =x1+k*(b/d) (注意k可以爲負數) 。這裏還要求x是最小整數解,x=x%(b/d) ,然後將x帶入原式的一式中。得到:
H=M1*(x1+k(b/d))+A1(k可以爲任意實數) 變形得到 :
H=(M1*(b/d)*K+(M1*x1+A1)
若我們令M3=M1*(b/d)A3=M1*x1+A1 則可以得到新的一項H≡A3 (mod M3) 這樣我們就將兩項何爲一項。只要我們這樣一直做下去就可以將n項合爲一項:
H≡An+1 (mod Mn+1) 即 H=k*Mn+1+An+1(最後一項)。
我們取k=0可得到H的 最小值爲H=Mn+1
注意:若An+1=0 則說明H剛好是M1,M2,,,,Mn的最小公倍數。

最後再講一下同餘模方程組的幾個定理:
a ≡c(mod b)
其實方程等價於 a – y*b = c,即a%b=c(y爲某一實數)。我們可以 統一的寫成:
(歐幾里得形式) a*x+b*y=c。
定理一:
設d = gcd(a, n), 假定對整數x’, y’, 有d = ax’ + ny’, 如果c%d=0,
則方程a ≡c(mod b)有一個解的值爲x0, 滿足:x0 = x’(b / d)(mod b)(b以內的解)
定理二:
假設方程a = c(mod b)有解, x0是方程的任意一個解, 則方程對模b恰有d個不同的解, 分別爲: xi = x0 + i * (b / d), 其中 i = 1,2,3……d – 1 ( d=gcd(a,b) )

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
int T,N;
int M[10],A[10];

int ex_gcd(int a,int b,int &x,int &y)
{//歐幾里得擴展
    if(b==0)
    {
        x=1; y=0;
        return a;
    }
    int d=ex_gcd(b,a%b,y,x);
    y=y-(a/b)*x;
    return d;
}
int gcd(int a,int b)
{//歐幾里得
    while(b)
    {
        int t=a;
        a=b;
        b=t%b;
    }
    return a;
}
int solve()
{
    int a,b,c,d,x,y,yu;
    a=M[0];
    yu=A[0];
    for(int i=1;i<N;i++)
    {//合併方程組
        b=M[i];
        c=A[i]-yu;
        d=ex_gcd(a,b,x,y);
        if(c%d)
          return -1;
        int B=b/d;
        x=((c/d*x)%B+B)%B;
        yu=x*a+yu;
        a=a*B;
    }
    if(yu)
        return yu;
    else
    {//最後餘數爲0的情況
        int key=M[0];
        for(int i=1;i<N;i++)
            key=key*M[i]/gcd(key,M[i]);
        return key;
    }
}
int main()
{
    scanf("%d",&T);
    for(int i=1;i<=T;i++)
    {
        scanf("%d",&N);
        for(int j=0;j<N;j++)
            scanf("%d",&M[j]);
        for(int j=0;j<N;j++)
            scanf("%d",&A[j]);
        printf("Case %d: %d\n",i,solve());
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章