高斯消元
高斯消元是對矩陣行化簡的算法,可以化成階梯型或者簡化階梯型。《線性代數及其應用》給出的步驟如下:
- 選取最左邊的非零列;
- 在該列中任意選取一個非零元素,通過對換變換將該行移到最上面;
- 通過倍加變換將下面的行的該列元素全部變成 \(0\);
- 暫時不管該行(即第一行),將剩下的子矩陣代回第一步進行循環,直到沒有非零列(矩陣內全為 \(0\))為止。此時該矩陣已經是階梯型矩陣;
- 對每個非零行先用倍加變換將先導元素上面的元素全部變成 \(0\),再用倍乘變換將先導元素變成 \(1\)。此時該矩陣已經是簡化階梯型矩陣。
復雜度瓶頸在於這若干個倍加變換。設矩陣 \(A\) 為 \(m\times n\),一次任意初等行變換的復雜度顯然都是 \(\mathrm O(n)\)。不管目標是階梯型還是簡化階梯型,對每個主元位置的產生,都需要將 \(\mathrm O(m)\) 個行的該列位置清零,所以處理一個主元位置的復雜度是 \(\mathrm O(mn)\)。然后一共有 \(\operatorname{rank}A\) 個主元,所以總復雜度是 \(\mathrm O(mn\operatorname{rank}A)\),有 \(\operatorname{rank}A\leq\min(m,n)\)(但可能有特殊情況使得矩陣的秩很小)。如果認為 \(m,n\) 同階,復雜度可以粗略的認為是 \(\mathrm O\!\left(n^3\right)\)。
但是書上是為了便於說明,才刻意搞的先化成階梯型,然后再化成簡化階梯型。事實上,如果你最終目標是簡化階梯型的話,完全可以把第 5 步合到第 3 步里一起搞。
高斯消元還有個比較重要的關於模數的問題,分三類討論一下:
- 無模數,那肯定是要在實數上搞了,也沒什么好說的。只是你看第五步,先把該列其它元素搞成 \(0\),再把自己搞成 \(1\),比反過來的精度誤差要小
(其實也沒小到哪去的說)。 - 模數為質數。這是比較爽的情況,不用考慮精度誤差,而且原來的線性運算在帶模之后仍然保持一切優良性質。有的時候有元素模成零了,但實際上不為零,於是可能出現的情況是在不模的時候矩陣的各列線性無關性、各列生成 \(\R^m\) 性要比模的時候好,但模的時候它就是沒有這些性質,不需要考慮別的。倍乘和倍加變換需要求逆元,看上去復雜度多個 log,但其實只需要求主元的逆元,於是可以把 log 給平行化掉,相當於復雜度不變。
- 任意模數。這就比較坑爹了,它破壞了一些線性代數性質:倍乘和倍加變換可能沒有逆操作,導致行等價關系不對稱,因某些數沒有逆元(你可能要說了,模數為質數的情況下,\(0\) 不也沒有逆元么?但實數情況下 \(0\) 也沒有逆元……所以並沒有損失性質)。但前輩們依然勉強找到了化成階梯型的方法:倍加變換的根本目的是把另一行的某列的位置變成 \(0\),避免求逆元的情況下可以類似歐幾里得算法的輾轉相除,終究能把目標位置消成 \(0\),並且只使用對換和倍加變換。這樣看似復雜度又多了個 log,但是每個主元的總輾轉次數其實是 log 的,所以這個 log 又可以平行化掉。至於要化成簡化階梯型,那是真的做不到了。第一,往上消零的話用上述輾轉相除不可取,因為不難發現輾轉相除的一個必要條件是對其它列都沒有要求,而往上消零還需要保持上面行的先導元素位置不變;第二,倍乘變換是真的沒辦法用奇技淫巧替代了……
mol ban tea:
- 解線性方程組,實數運算,如果最后一列有主元位置就無解,否則如果有自由變量就無限解,否則唯一解,code。
- 矩陣求逆,只需要放個 \(I_n\) 在右邊,然后對左邊行化簡即可,質數模數不慌,code。
- 行列式求值,任意模數,但是求行列式只需要化成階梯型然后把主對角線乘起來,不慌,注意對換操作只有交換不同行才需要取反,另外這題很卡常,倍加變換如果倍數是 \(0\) 就
return
這個剪枝加上去就能過了,code。
線性基
給定一些數,考慮所有子集的異或和的集合。異或的話可以把數按二進制位拆成向量,那么異或就是 \(\bmod 2\) 意義下的加法。所以這個集合是線性空間,線性基就是這個集合的一組基。考慮求這組基。
我們知道,將一個矩陣 \(A\) 行化簡,非零行是 \(\operatorname{Row}A\) 的一組基,主元列的列號對應的原來的向量是 \(\operatorname{Col}A\) 的一組基。所以我們求 \(A\) 的列的基有兩種方法:一種是看成 \(\operatorname{Row}A^{\mathrm T}\),一種是看成 \(\operatorname{Col}A\)。我們采用前者求線性基,因為求出來有良好的性質(階梯型 / 簡化階梯型的性質唄),況且本來就不需要基包含於原集合。
線性基一般采用動態高斯消元的方法,動態插入數,按道理插入一次是 \(\mathrm O\!\left(\log^2v\right)\)(其中 \(v\) 是值域)的,但是行變換可以直接異或(相當於 bitmask
優化),所以插入一次是 \(\mathrm O(\log v)\) 的。我們也選擇不用 vector
來維護,而對每個主元位置維護對應的行,如果暫時是空的就是 \(0\),這樣好寫很多,不用 insert
之類,還可以直接提取想要的主元位置對應行。而且我們並不需要消成簡化階梯型,普通階梯型就可以了。折騰一番之后代碼變成了這個鬼樣子(是不是炒雞好寫!):
void insert(int x){
for(int i=logv;~i;i--)if(x>>i&1)
if(b[i])x^=b[i];
else{b[i]=x;break;}
}
這種方法構造出來的線性基的特殊性質就是階梯型,貪心起來特爽。線性基大小顯然是 \(\mathrm O(\log v)\) 吧。
能解決的一些問題(都特別簡單啦,都是線代基礎):
- 數集線性相關性:線性基大小是否等於原數集。
- 生成空間大小:那顯然就是 \(2^{|\mathcal B|}\)。
- 任選子集,異或和異或上 \(x\) 的最大 / 小值:從高往低按位貪心,跟據階梯型性。
- 異或和等於 \(x\) 的子集數:按高斯消元解方程模擬。
- 異或和不超過 \(x\) 的子集數:按數位 DP 統計方式。
矩陣樹定理
用來計算無向圖生成樹個數或者有向圖的內 / 外向樹個數。
下面允許圖有重邊,但不允許有自環:
- 無向圖:設 \(D\) 為無向圖的度數矩陣(對角線是度數,其它位置是 \(0\)),\(T\) 為鄰接矩陣,基爾霍夫矩陣為 \(K=D-T\),則生成樹數量為 \(K\) 的任意一個 \((i,i)\) 余子式(都相等)。
- 有向圖:設 \(iD\) 為入度矩陣,\(oD\) 為出度矩陣,\(T\) 依然是鄰接矩陣,基爾霍夫矩陣 \(iK=iD-T,oK=oD-T\),則以 \(r\) 為根的外向樹個數為 \(iK\) 的 \((r,r)\) 余子式,內向樹個數為 \(oK\) 的 \((r,r)\) 余子式。
證明的話就略了吧。網上找到的主要有兩種證法,一種是基於關聯矩陣和柯西-比內公式的證法,較能反應矩陣樹定理的本質,但只能證無向圖情況;另一種是對邊數歸納,有向無向都能證,但不能反應本質。
如果圖中有自環要手動去掉,因為有沒有自環對生成樹數量毫無影響,而矩陣樹算法算上自環的話明顯會受到干擾。如果要求有向圖以所有點為根的內 / 外向樹的個數和,那只能四方地算,因為 \(iK,oK\)(包括無向圖的 \(K\))都不滿秩,無法套用 \(A^*=|A|A^{-1}\) 來算伴隨矩陣。有一個利用矩陣樹定理反過來證明不滿秩的方法:考慮再加一個點不連邊,此時圖不連通,生成樹數量顯然為 \(0\),用矩陣樹定理計算的話去掉新加點所在行列得到原矩陣,行列式就為 \(0\) 了。
擴展:它可以帶權,此時度數矩陣、鄰接矩陣里裝的都是邊權和,這樣矩陣樹定理算出來的是所有生成樹的邊權積的邊權和(仔細想一下,重邊也恰可以用這個擴展來理解)。邊權甚至可以為多項式(GF)或者其它奇怪的東西(需要定義加法、乘法,並且有某些良好的性質)放到行列式里去消元,至於為什么能這樣以后再深究。
這樣一來我們有辦法計算所有生成樹的邊權和的和(其實可以枚舉邊縮邊跑矩陣樹,但那樣復雜度最高是五方的):注意到 \(\prod\limits_{e\in T}(w_ex+1)\bmod x^2\) 的一次項就是生成樹 \(T\) 的邊權和。那么可以矩陣樹套一次多項式,加減乘都是顯然的,除法的話就是 \(\bmod x^2\) 意義下求逆,稍微解個方程知道 \(\dfrac{ax+b}{cx+d}\equiv\dfrac{ad-cb}{d^2}x+\dfrac bd\),如果 \(c\neq 0,d=0\) 就掛了,那怎么辦呢?解決方法是:如果該列只有 \(ax\) 形狀的多項式,那直接可以消,否則一定能找到 \(ax+b(b\neq 0)\)(但大多數情況下這種情況不會出現,概率是不是太小了)。
BEST 定理
實現基於矩陣樹,但理論不基於矩陣樹的一個定理,用來求有向圖歐拉回路數量。
定理內容:連通歐拉圖中從 \(s\) 出發的歐拉回路數量為 \(T\deg_s\prod\limits_{i\in V}(\deg_i-1)!\),其中 \(T\) 是以 \(s\) 為根的內 / 外向樹個數(由於是歐拉圖,所以所有點入度等於出度,易得兩者相等)。
BEST 定理的證明
對一棵內向樹,給每個點的出邊集指定一個順序,滿足對非根節點它的樹邊出邊是最后一個,構造路徑:從 \(s\) 出發每到一個點沿着順序中最前面還沒走的邊走下去,直到走不下去了(當前點的出邊全部被走過了)停止。我們只需要證明對每個 (內向樹, 順序) 二元組構造出來的路徑是歐拉回路,以及每個起點為 \(s\) 的歐拉回路有恰好一個二元組映射到它(即 (內向樹, 順序) 二元組與起點為 \(s\) 的歐拉回路形成雙射)。
為證明前者,只要證不會在非根節點走不下去,以及在根節點走不下去時已經訪問完所有邊。
- 對非根節點,每次剛到達它的時候,顯然它的已訪問入邊比已訪問出邊多 \(1\)。而由於原圖是歐拉圖,所以入度等於出度,此時必然還有出邊沒訪問,不可能走不下去。事實上這個結論可以脫離「內向樹」而存在,就是說在歐拉圖上隨便走(每條邊只能訪問一次)都不可能在非起點節點停下。
- 如果在根節點停下時還存在邊未訪問,任何邊都一定是某個點的出邊(廢話),那么該點的出邊順序中位於這條邊后面的邊也沒被訪問。而樹邊一定在順序的最后,所以必定存在某條樹邊 \((x,fa_x)\) 未被訪問。那么 \(fa_x\) 的入邊沒有被訪問完,跟據入度等於出度,它一定有出邊沒訪問完,所以 \(\left(fa_x,fa_{fa_x}\right)\) 也一定沒被訪問。如此一直向上蔓延,知道根節點有入邊沒被訪問,跟據入度等於出度,它一定有出邊還沒訪問,就不會停下來,矛盾!
接下來證明后者。那顯然對某個起點為 \(s\) 的歐拉回路,最多只可能有一棵內向樹映射到它,只要證取出每個非起點節點最后訪問的出邊一定構成內向樹即可。一個必要條件是基圖構成無向有根樹,而跟據每個非根節點恰有一條出邊易證無向有根樹恰好存在一種合法的定向方式,並且得到內向樹,所以也是充分條件。\(n\) 個點 \(n-1\) 條無向邊,只要證無環即可知它們構成樹。如果有環的話,跟據每個非根節點恰有一條出邊可知無向環還原成有向邊之后,一定是有向環,而且根節點不會在環上(因為根沒有出邊)。取這個有向環上在歐拉回路中第一條被訪問的邊 \((x,y)\),那么環上其他邊還沒被訪問,此時 \(x\) 的樹邊出邊已經被訪問,說明 \(x\) 所有出邊都已經被訪問,跟據入度等於出度知道 \(x\) 的所有入邊也已被訪問,而顯然存在環上的一條未訪問的 \(x\) 的入邊,矛盾!
BEST 定理得證!
如果不限定起點的話,從 \(s\) 出發的歐拉回路每 \(\deg_s\) 形成一個循環同構,所以數量是 \(T\prod\limits_{i\in V}(\deg_i-1)!\)(無論選取哪個 \(s\) 都得到這個結果,恰體現了合理性)。如果不算循環同構的話,每 \(m\) 個歐拉回路構成一個循環同構,總數是 \(Tm\prod\limits_{i\in V}(\deg_i-1)!=T\sum\limits_{i\in V}\deg_i\prod\limits_{i\in V}(\deg_i-1)!\),很合理對否。
考慮實現的時候需要注意的事情:
- 需要判斷是否歐拉圖,不是歐拉圖的話使用 BEST 會出錯,應該直接輸出
0
跑路。 - 自環問題,這個在矩陣樹的時候就說過了,要手動去自環消元,但是歐拉回路數量計算公式 \(\deg\) 要算上自環。
- 孤立點問題,出現孤立點的時候導致圖不連通,導致 \(T=0\),但是是否歐拉圖只關心邊的連通性。此時應該把孤立點直接刪除,一個比較方便的操作是直接把基爾霍夫的 \(K_{i,i}\) 由 \(0\) 改成 \(1\),跟據行列式的按行 / 列展開易證正確性。但是此時如果指定起點是孤立點的時候,答案便是 \(0\) 了;但是又有一個特例(煩吧!):圖上沒有邊時,答案是 \(1\)。
lgv 引理
在一個 DAG 上,有一個起點集合 \(A\) 和終點集合 \(B\),滿足 \(|A|=|B|\)。一條路徑 \(P\) 上邊權的積是 \(P\) 的權值 \(w(P)\)。設 \(e(x,y)=\sum\limits_{P:x\to y}w(p)\),即 \(x\to y\) 所有路徑的權值和。設 \(A\to B\) 的一個不相交路徑方案為對於任意的 \(1\sim |A|\) 排列 \(p\) 的 \(\{P_i:A_i\to B_{p_i}\}\) 這樣一個兩兩不相交的路徑集合,它的權值為 \(w(P)=\prod\limits_{i=1}^{|A|}w(P_i)\)。
lgv 引理:設 \(|A|\) 階方陣 \(M\) 滿足 \(M_{i,j}=e(A_i,B_j)\),則有
即 \(A\to B\) 的所有不相交路徑方案的權值代數和。
證明:將行列式按第一定義展開,它顯然等於 \(A\to B\) 所有路徑方案(不保證相交)的權值代數和。那為什么它又等於不相交的呢?我們只需要在偶相交路徑方案集合和奇相交路徑方案集合之間建立一個雙射,並且每組映射滿足原像和像權值相等,這樣所有相交路徑方案就可以抵消掉了。構造映射的方案很簡單:對任意相交路徑方案,找到第一個交點(這里第一個指的是以相交甲方為第一關鍵字、相交乙方為第二關鍵字、交點離兩個起點的距離為第三關鍵字的第一個),將交點后的兩條路徑互換身份(就像 P1007 一樣),這樣排列中交換了元素,奇偶性就變化了。易證雙射性。
如果想計數的話,只需要把邊權都設成 \(1\) 即可。與矩陣樹定理類似,邊權可以是多項式或其它類型,放到行列式里跑。這樣一來把邊權積變成邊權和顯然也是可以做的了。甚至你發現 \(e\) 的計算和行列式計算是分開的,所以甚至可以解決「路徑權值是邊權積,路徑方案權值是路徑權值和」這樣的問題。
\(e\) 函數一般很好算,畢竟是 DAG,復雜度瓶頸一般在高斯消元。lgv 引理的題有一大半都是只有排列 \(p=[1,2,\cdots,|A|]\) 的不相交路徑方案權值非零(包括某些網格圖的題),它們是用求代數和的方法來求數值和,雖然有點殺雞用牛刀的感覺,但並沒有更好的方法。洛谷上的模板題感覺並不模板,就當習題了。