[學習筆記] 高維字首和
阿新 • • 發佈:2020-07-28
演算法用途
求
\[\sum_{i = 0}^{n} [i\ \&\ n = i] a_i \]
(實際上, 這只是高維字首和的一種特殊形式, 即每一維的大小都為 2.)
演算法過程
我們計算矩陣字首和時, 通常用的是容斥的方法.
設當前要計算 \(D\) 維字首和, 容斥的複雜度為
\[\sum_{i = 0}^n \binom{D}{i} = 2^D \]
當維數過大時顯然不行.
我們考慮計算矩陣字首和的另一種方法 : 先算出每一列的字首和, 然後再把每一列的字首和加起來即可得到一個矩陣的字首和.
我們來分析一下這個方法的實質 : 矩陣是一個二維結構, 我們用二元組 \((i,j)\)
考慮把這個方法推廣到 \(D\) 維 : 先求出控制 前 \(D\) 維 的字首和, 再求出控制 前 \(D - 1\) 維 的字首和, 再求出控制 前 \(D - 2\) 維的字首和 ...... 最終求出控制 前 \(0\) 維 的字首和, 即為 \(D\) 維字首和.
用式子來表示就是 : 設 \(sum[i][s]\) 為控制了 前 \(D - i\) 維的點 \(s\) 的字首和 (即 \(sum[i][s]\) 內統計到的點的 前 \(D - i\)
程式碼實現
由於在轉移過程中, \(s'\) 嚴格小於 \(s\), 所以 \(sum\) 的第一維可以用滾動陣列優化.
這裡只給出了每一維的大小都等於 \(2\) 的情況的程式碼, 即「演算法用途」中給出的形式.
for (int i = 0; i < n; i++) for (int s = 0; s < 1 << n; s++) if (s >> i & 1) sum[s] += sum[s ^ (1 << i)];
當然, 由於這裡每一維的大小都為 \(2\), 所以你把中間那個迴圈的順序反過來也行, 而更一般的情況下就要嚴格按照順序轉移了.
有什麼沒看懂或者表達不嚴謹的地方歡迎留言