1. 程式人生 > >hihocoder 1303 : 數論六·模線性方程組

hihocoder 1303 : 數論六·模線性方程組

hihocoder 1303

描述

小Ho:今天我聽到一個挺有意思的故事!

小Hi:什麼故事啊?

小Ho:說秦末,劉邦的將軍韓信帶領1500名士兵經歷了一場戰鬥,戰死四百餘人。韓信為了清點人數讓士兵站成三人一排,多出來兩人;站成五人一排,多出來四人;站成七人一排,多出來六人。韓信立刻就知道了剩餘人數為1049人。

小Hi:韓信點兵嘛,這個故事很有名的。

小Ho:我覺得這裡面一定有什麼巧妙的計算方法!不然韓信不可能這麼快計算出來。

小Hi:那我們不妨將這個故事的數學模型提取出來看看?

小Ho:好!

<小Ho稍微思考了一下>

小Ho:韓信是為了計算的是士兵的人數,那麼我們設這個人數為x。三人成排,五人成排,七人成排,即x mod 3, x mod 5, x mod 7。也就是說我們可以列出一組方程:

x mod 3 = 2
x mod 5 = 4
x mod 7 = 6

韓信就是根據這個方程組,解出了x的值。

小Hi:嗯,就是這樣!我們將這個方程組推廣到一般形式:給定了n組除數m[i]和餘數r[i],通過這n組(m[i],r[i])求解一個x,使得x mod m[i] = r[i]。

小Ho:我怎麼感覺這個方程組有固定的解法?

小Hi:這個方程組被稱為模線性方程組。它確實有固定的解決方法。不過在我告訴你解法之前,你不如先自己想想怎麼求解如何?

小Ho:好啊,讓我先試試啊!

輸入

第1行:1個正整數, N,2≤N≤1,000。

第2..N+1行:2個正整數, 第i+1行表示第i組m,r,2≤m≤20,000,000,0≤r<m。

計算過程中儘量使用64位整型。

輸出

第1行:1個整數,表示滿足要求的最小X,若無解輸出-1。答案範圍在64位整型內。

樣例輸入

3
3 2
5 3
7 2

樣例輸出

23

Solution

中國剩餘定理,注意解不定方程組的一些細節

Code

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <map>
#include <vector>
#include <queue>
#define LL long long
#define L 1010
using namespace std;

int n, m[L], r[L];
LL M, R;

inline LL gcd(LL x, LL y) {
  return (x % y == 0) ? y : gcd(y, x % y);
}

inline void exgcd(LL a, LL b, LL& x, LL& y) {
  if (!b) {x = 1, y = 0;}
  else {
    exgcd(b, a % b, y, x);
    y -= (a / b) * x;
  }
}

inline LL solve() {
  M = m[1], R = r[1];
  for (int i = 2; i <= n; ++i) {
    LL d = gcd(M, m[i]), c = r[i] - R;
    if (c % d) return -1;
    LL x, y;
    exgcd(M / d, m[i] / d, x, y);
    x = (c / d * x) % (m[i] / d);
    R += x * M;
    M = M / d * m[i];
    R %= M;
  }
  if (R < 0) R += M;
  return R;
}

int main() {
  scanf("%d", &n);
  for (int i = 1; i <= n; ++i) scanf("%d %d", &m[i], &r[i]);
  printf("%lld\n", solve());
  return 0;
}

Summary

注意需要判斷無解的情況,所以不能一味的用通解