1. 程式人生 > 實用技巧 >[學習筆記] 高維字首和

[學習筆記] 高維字首和

演算法用途

\[\sum_{i = 0}^{n} [i\ \&\ n = i] a_i \]

(實際上, 這只是高維字首和的一種特殊形式, 即每一維的大小都為 2.)


演算法過程

我們計算矩陣字首和時, 通常用的是容斥的方法.

設當前要計算 \(D\) 維字首和, 容斥的複雜度為

\[\sum_{i = 0}^n \binom{D}{i} = 2^D \]

當維數過大時顯然不行.


我們考慮計算矩陣字首和的另一種方法 : 先算出每一列的字首和, 然後再把每一列的字首和加起來即可得到一個矩陣的字首和.

我們來分析一下這個方法的實質 : 矩陣是一個二維結構, 我們用二元組 \((i,j)\)

來表示矩陣上的點. 那麼每一列的字首和實質上是控制了 \(j\) 這一維後計算的字首和, 而一個矩陣的字首和就是 \(i,j\) 這兩維都不進行控制的字首和.

考慮把這個方法推廣到 \(D\) 維 : 先求出控制 前 \(D\) 維 的字首和, 再求出控制 前 \(D - 1\) 維 的字首和, 再求出控制 前 \(D - 2\) 維的字首和 ...... 最終求出控制 前 \(0\) 維 的字首和, 即為 \(D\) 維字首和.

用式子來表示就是 : 設 \(sum[i][s]\) 為控制了 前 \(D - i\) 維的點 \(s\) 的字首和 (即​ \(sum[i][s]\) 內統計到的點的 前 \(D - i\)

維 都和 \(s\) 相同), 那麼 \(sum[i][s] = sum[i - 1][s] + sum[i][s']\), 其中 \(s'\) 為前 \(i - 1\) 維都與 \(s\) 相同 且 第 \(i\) 維比 \(s\)\(1\) 的點. 聯想一下矩陣字首和就很好理解了.


程式碼實現

由於在轉移過程中, \(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\), 所以你把中間那個迴圈的順序反過來也行, 而更一般的情況下就要嚴格按照順序轉移了.


有什麼沒看懂或者表達不嚴謹的地方歡迎留言