高維字首和(SOS DP)

小超手123發表於2024-03-13

引入方法

在討論高維字首和前,不妨先回顧以下二維字首和,一種寫法是:

for(int i = 1; i <= w; i++)
	for(int j = 1; j <= w; j++)
        sum[i][j] += sum[i][j - 1]
for(int i = 1; i <= w; i++)
	for(int j = 1; j <= w; j++)
        sum[i][j] += sum[i - 1][j]       

推廣到 \(n\) 維就是:

for(int x1 = 1; x1 <= w; x1++)
    for(int x2 = 1; x2 <= w; x2++)
        ...
        for(int xn = 1; xn <= w; xn++)
            sum[x1][x2]...[xn] += sum[x1 - 1][x2]...[xn]
for(int x1 = 1; x1 <= w; x1++)
    for(int x2 = 1; x2 <= w; x2++)
        ...
        for(int xn = 1; xn <= w; xn++)
            sum[x1][x2]...[xn] += sum[x1][x2 - 1]...[xn]
...
for(int x1 = 1; x1 <= w; x1++)
    for(int x2 = 1; x2 <= w; x2++)
        ...
        for(int xn = 1; xn <= w; xn++)
            sum[x1][x2]...[xn] += sum[x1][x2]...[xn - 1]

解決問題

它有什麼用嗎?看這個問題:

計算所有的 \(f(S)=\sum_{T \in S} g(T)\)

暴力列舉子集時間複雜度為 \(O(3^n)\)。其中 \(n = |S|\)

但使用高維字首和可以最佳化到 \(O(n2^n)\)

不難發現,對於每個元素,只有選與不選兩種可能,可以記作 \(0/1\)。不妨看作有 \(n\) 維,每維的空間大小都是 \(2\)。對於一個 \(1\),可以允許 \(0/1\);對於一個 \(0\),可以允許 \(0\)

實際上就是在求一個高維字首和,我們可以狀態壓縮成一個二進位制數,然後模擬上述過程即可。

程式碼實現如下:

for(int i = 0; i < n; i++)
    for(int j = 0; j < (1 << n); j++)
        if(j & (1 << i)) f[j] += f[j ^ (1 << i)];

例題講解

一、P5495 【模板】Dirichlet 字首和:
題意:

給定一個長度為 \(n\) 的數列 \(a_1,a_2,a_3,\dots,a_n\)

現在你要求出一個長度為 \(n\) 的數列 \(b_1,b_2,b_3,\dots,b_n\),滿足:

\[b_k=\sum_{i|k}a_i \]

\(n \le 2 \times 10^7\)

分析:

考察對高維字首和的理解。

對於一個 \(a\),它對 \(b\) 有貢獻當且僅當 \(p_{i} \le q_{i}\),其中 \(a=\prod_{P \in prim} P^{p_{i}},b=\prod_{P \in prim} P^{q_{i}}\)

把每個質數看成一維,那麼就是在求一個高維字首和。可以直接用這個數的值表示它的狀態。

列舉每個質數,加上除去這個質數的答案。

具體來說就是這樣算:

for(int i = 2; i <= n; i++) {
	if(f[i]) continue; //f[i]表示是否是合數
	for(int j = 1; i * j <= n; j++)
		a[i * j] += a[j]; 
}
二、[ARC100E] Or Plus Max
題意:

給你一個長度為 \(2^n\) 的序列 \(a\),每個\(1\le K\le 2^n-1\),找出最大的 \(a_i+a_j\)\(i \mathbin{\mathrm{or}} j \le K\)\(0 \le i < j < 2^n\))並輸出。

\(n \le 18,a_{i} \le 10^9\)

分析:

對於每個 \(K\),我們可以去求 \(i \in K\) 的最大值與次大值,作為 \(i\)\(j\),然後對 \(K-1\) 的答案取 \(\max\) 即可。

容易發現這樣做不存在一對 \(i,j\) 本來不能被統計而被統計了,也不存在本來應該被統計而沒被統計。

高維字首 \(\max\) 即可。

三、[CF1208F] Bits And Pieces
題意:

給定 \(n\) 個數的陣列 \(a\),找到 $i\lt j\lt k $ 的 \(i,j,k\),使得 \(a_i|(a_j \& a_k)\) 最大。

\(n \le 10^6,a_{i} \le 2 \times 10^6\)

分析:

考慮列舉 \(i\),然後從高往低位貪心。

  • \(a_{i}\) 的第 \(j\) 位為 \(1\),沒有任何限制條件,當前答案直接加上 1<<j 即可。
  • \(a_{i}\) 的第 \(j\) 位為 \(0\)。增加限制條件,利用高維字首和維護一個滿足這個限制條件的數的位置的最大值與次大值,如果能找到更新答案,否則恢復成原來的限制條件。

時間複雜度 \(O(n \log w+w \log w)\)

相關文章