同余模方程学习小记 [FINISHED]
什么是同余模呢?比如21%6=3,21%8=5,21%11=10,就是给出(6,3)(8,5)(11,10)这样的数对来求一个最小的正整数X,满足:
X%6=3;
X%8=5;
X%11=10。
怎么解决这个问题呢?首先我们将这样的数对表示为(m,r),设有两个(m1,r1),(m2,r2),设答案为X,那么显然有X=m1*k1+r1,X=m2*k2+r2 ,因此得到:m1*k1+r1=m2*k2+r2,即:m1*k1-m2*k2=r2-r1。设x=k1,y=-k2,c=r2-r1,上式即为:m1*x+m2*y=c。我们可以用扩展欧几里得求出x和y以及m1和m2的最大公约数d,如果c不能整除d则显然这样的x和y是不存在的。下面的讨论我们假设c能够整除d.我们知道我们求出x和y满足:m1*x+m2*y=d。那么我们可以求出x0和y0满足m1*x0+m2*y0=c,显然x0=c/d*x,y0=c/d*y,并且我们知道,X、Y的通项公式为:
X=x0+(m2/d)*k
Y=y0-(m1/d)*k
因此为求出最小的正整数X,我们总能用m2/d来调节x0使X最小。那么,若给出的是超过两组的(m,r)呢?这样解决:将r0=X(X就是用前两组求出的X),m0=m1*m2/d(即m0是m1和m2的最小公倍数),那么若有超过两组的(m,r),最后的答案ans必满足:ans%m0=r0。因为ans=m0*k+r0,因此ans%m1=(m0*k+r0)%m1=r0%m1=X%m1=r1,同理对(m2,r2)也是。这样我们就将前两组合并成新的一组(m0,r0)。这样依次合并,
#include <vector> template <typename INT_TYPE = long long> class CongruenceSolver { public: // The input are {(6, 3), (8, 5), (11, 10)} INT_TYPE Solve(const std::vector<std::pair<INT_TYPE, INT_TYPE>> &pi) { INT_TYPE a = pi.front().second; INT_TYPE m = pi.front().first; for (decltype(pi.size()) i = 1; i < pi.size(); ++i) { INT_TYPE x, y; INT_TYPE d = EXGcd(m, pi[i].first, x, y); INT_TYPE c = pi[i].second - a; if (c % d != 0) { return -1; } INT_TYPE t = pi[i].first / d; x = (c / d * x % t + t) % t; a = m * x + a; m = m * pi[i].first / d; } return a; } private: INT_TYPE EXGcd(INT_TYPE a, INT_TYPE b, INT_TYPE &x, INT_TYPE &y) { if (0 == b) { x = 1; y = 0; return a; } INT_TYPE d = EXGcd(b, a % b, x, y); INT_TYPE t = x; x = y; y = t - a / b * y; return d; } };