前言
閱讀本文前,推薦先學一下中國剩餘定理。其實不學也無所謂,畢竟兩者沒啥關係
擴充套件CRT
我們知道,中國剩餘定理是用來解同餘方程組
$$\begin{cases}x\equiv c_{1}\left( mod\ m_{1}\right) \\ x\equiv c_{2}\left( mod\ m_{2}\right) \\ \ldots \\ x\equiv c_r\left( mod\ m_r\right) \end{cases}$$
但是有一個非常令人不爽的事情就是它要求$m_1,m_2\ldots,m_r$兩兩互素
如果某個毒瘤出題人偏要求它們部互素呢?
其實也有解決的辦法
就是把出題人吊起來幹一頓用擴充套件中國剩餘定理
擴充套件中國剩餘定理跟中國剩餘定理沒半毛錢關係,一個是用擴充套件歐幾里得,一個是用構造
首先我們還是從簡單入手,考慮一下如果同餘方程組只有兩個式子的情況
$x\equiv c_{1}\left( mod\ m_{1}\right) \\ x\equiv c_{2}\left( mod\ m_{2}\right)$
將兩個式子變形
$x=c_{1}+m_{1}k_{1}\\ x=c_{2}+m_{2}k_{2}$
聯立
$c_{1}+m_{1}k_{1}=c_{2}+m_{2}k_{2}$
移項
$m_{1}k_{1}=c_{2}-c_{1}+m_{2}k_{2}$
我們用$(a,b)$表示$a,b$的最大公約數
在這裡需要注意,這個方程有解的條件是
$\left( m_{1},m_{2}\right) |\left( c_{2}-c_{1}\right)$,因為後面會用到$\dfrac {\left( c_{2}-c_{1}\right) }{\left( m_{2},m_{1}\right) }$這一項,如果不整除的話肯定會出現小數。
對於上面的方程,兩邊同除$(m_1,m_2)$
$$\dfrac {m_{1}k_{1}}{\left( m_{1},m_{2}\right) }=\dfrac {c_{2}-c_{1}}{\left( m_{1},m_{2}\right) }+\dfrac {m_{2}k_{2}}{\left( m_{1},m_{2}\right) }$$
$$\dfrac {m_{1}}{\left( m_{1},m_{2}\right) }k_{1}=\dfrac {c_{2}-c_{1}}{\left( m_{1},m_{2}\right) }+\dfrac {m_{2}}{\left( m_{1},m_{2}\right) }k_{2}$$
轉換一下
$$\dfrac {m_{1}}{\left( m_{1},m_{2}\right) }k_{1} \equiv \dfrac {c_{2}-c_{1}}{\left( m_{1},m_{2}\right) } (mod\ \dfrac {m_{2}}{\left( m_{1},m_{2}\right) })$$
此時我們已經成功把$k_2$消去了。
同餘式兩邊同除$\dfrac {m_{1}}{\left( m_{1},m_{2}\right) }$
$$k_1\equiv inv({m_1\over(m_1,m_2)},{m_2\over (m_1,m_2)})*{(c_2-c_1)\over (m_1,m_2)}\pmod {{m_2\over(m_1,m_2)}}$$
$inv(a,b)$表示$a$在模$b$意義下的逆元
$$k_1=inv({m_1\over(m_1,m_2)},{m_2\over (m_1,m_2)})*{(c_2-c_1)\over (m_1,m_2)}+{{m_2\over (m_1,m_2)}}*y$$
接下來怎麼辦呢?這個式子已經化到最簡了。。
不要忘了,我們剛開始還有兩個式子。我們把$k_1$待回去!
$$x=inv({m_1\over(m_1,m_2)},{m_2\over (m_1,m_2)})*{(c_2-c_1)\over (m_1,m_2)}*m_1+y{{m_1m_2\over (m_1,m_2)}}+c_1$$
$$x\equiv inv({m_1\over(m_1,m_2)},{m_2\over (m_1,m_2)})*{(c_2-c_1)\over (m_1,m_2)}*m_1+c_1\pmod {{m_1m_2\over (m_1,m_2)}}$$
此時,整個式子中的元素我們都已經知道了
具體一點,這個式子可以看做是$$x\equiv c\pmod m$$
其中$$c=(inv({m_1\over (m_1,m_2)},{m_2\over (m_1,m_2)})*{(c_2-c_1)\over (m_1,m_2)})\%{m_2\over (m_1,m_2)}*m_1+c_1$$
$$m={m_1m_2\over (m_1,m_2)}$$
推廣一下
我們每次把兩個同餘式合併,求解之後得到一個新的同餘式。再把新的同餘式和其他的聯立,最終就可以求出滿足條件的解
程式碼
update in 2018.10.11:這份程式碼可能在較強的資料下出現乘爆long long的情況(我記得NOI2018Day2T1就是因為這個過不了大樣例)
直接把#define LL long long 改成#define LL __int128就好了
#include<iostream> #include<cstdio> #define LL long long using namespace std; const LL MAXN = 1e6 + 10; LL K, C[MAXN], M[MAXN], x, y; LL gcd(LL a, LL b) { return b == 0 ? a : gcd(b, a % b); } LL exgcd(LL a, LL b, LL &x, LL &y) { if (b == 0) {x = 1, y = 0; return a;} LL r = exgcd(b, a % b, x, y), tmp; tmp = x; x = y; y = tmp - (a / b) * y; return r; } LL inv(LL a, LL b) { LL r = exgcd(a, b, x, y); while (x < 0) x += b; return x; } int main() { #ifdef WIN32 freopen("a.in", "r", stdin); #else #endif while (~scanf("%lld", &K)) { for (LL i = 1; i <= K; i++) scanf("%lld%lld", &M[i], &C[i]); bool flag = 1; for (LL i = 2; i <= K; i++) { LL M1 = M[i - 1], M2 = M[i], C2 = C[i], C1 = C[i - 1], T = gcd(M1, M2); if ((C2 - C1) % T != 0) {flag = 0; break;} M[i] = (M1 * M2) / T; C[i] = ( inv( M1 / T , M2 / T ) * (C2 - C1) / T ) % (M2 / T) * M1 + C1; C[i] = (C[i] % M[i] + M[i]) % M[i]; } printf("%lld\n", flag ? C[K] : -1); } return 0; }
再放道裸題
http://acm.hdu.edu.cn/showproblem.php?pid=1573