1. 程式人生 > >HDU 1573 X問題 (中國剩餘定理 模線性方程組)

HDU 1573 X問題 (中國剩餘定理 模線性方程組)

思路:

方法1:

問題可以轉化為求模線性方程組。
設要求得的滿足方程組的最小正整數為n;
n=b1(moda1)
n=b2(moda2)
可以得到: n=b1+xa1=b2+ya2
變形之後:
xa1ya2=b2b1(1)
對式(1),我們可以看作 xa1=b2b1(moda2)
對於這個式子,我們可以用擴充套件歐幾里得演算法求得x的最小整數解。所以最小正整數為 n0=b1+a1x
我們求解出來的n0加上k倍的a2絕對滿足題意,但是卻有可能少解,原因就是(1)式左右兩邊說不定可以約分,這樣的話作為週期的 a2 就不對了。所以要這樣解決:
n=n0+

ka2/gcd(a1,a2)(k)(moda1a2/gcd(a1,a2))
以上,我們就完成了對兩個模線性方程的合併,接下來只要逐一進行合併,到最後只剩一個模線性方程時,就能很輕易地求解了。

#include <iostream>
#include <cstdio>
typedef long long int lli;

using namespace std;

int a[12];
int b[12];

lli egcd(lli a,lli b,lli &x,lli &y){
    if(b == 0){
        x = 1
; y = 0; return a; } lli ans = egcd(b,a%b,x,y); lli temp = x; x = y; y = temp - a/b*x; return ans; } int main() { int t; int n,num; cin>>t; while(t--){ scanf("%d%d",&n,&num); for(int i = 1;i <= num;i++){ scanf
("%d",a+i); } for(int i = 1;i <= num;i++){ scanf("%d",b+i); } lli b1 = b[1]; lli a1 = a[1]; lli x,y; int flag = 0; for(int i = 2;i <= num;i++){ lli gcd = egcd(a1,a[i],x,y); if((b[i]-b1) % gcd != 0){//這是模線性方程有整數解的充要條件 flag = 1; break; } lli temp = a[i]/gcd; x = x * (b[i]-b1)/gcd; // 也是約分的問題 x = (x%(temp) + temp) % (temp); b1 = b1 + a1*x; a1 = a1 * temp;// a1 = a1*a[i]/gcd } if(flag == 1 || n < b1){ puts("0"); } else{ lli ans = (n-b1)/a1 + 1; if(b1 == 0) ans--; printf("%I64d\n",ans); } } return 0; }

方法2:

先求出所有 ai 的最小公倍數lcm,我們可知,如果有某個數x滿足題目的性質的話,那麼x+lcm也一定滿足題目的描述。所以我們對於n%lcm+1到n%lcm + lcm這個區間裡暴力搜尋是否有滿足條件的解。如果有那麼sum += n/lcm,注意:每有一個,都要加一回n/lcm。
最後在1到n%lcm這個區間在找有沒有符合的數,有的話,+1。

#include <iostream>
#include <cstdio>
typedef long long int lli;

using namespace std;

int a[12];
int b[12];

lli gcd(lli a,lli b){
    return b == 0 ? a : gcd(b,a%b);
}

int main(){
    int t;
    int n,num;
    cin>>t;
    while(t--){
        scanf("%d%d",&n,&num);
        lli lcm = 1;
        for(int i = 1;i <= num;i++){
            scanf("%d",a+i);
            lcm = lcm * a[i] / gcd(lcm,a[i]);
        }
        for(int i = 1;i <= num;i++){
            scanf("%d",b+i);
        }
        lli ans = 0;
        int flag;
        for(int i = n%lcm + 1;i <= n%lcm + lcm;i++){
            flag = 1;
            for(int j = 1;j <= num;j++){
                if(i%a[j] != b[j]){
                    flag = 0;
                    break;
                }
            }
            if(flag == 1){
                ans += n/lcm;
            }
        }


        for(int i = 1;i <= n%lcm;i++){
            flag = 1;
            for(int j = 1;j <= num;j++){
                if(i%a[j] != b[j]){
                    flag = 0;
                    break;
                }
            }
            if(flag == 1){
                ans += 1;
            }
        }
        printf("%I64d\n",ans);
    }
return 0;}