淺記高維字首和

studentDL發表於2024-03-19

考慮如下問題:
\(y\subset x\leftrightarrow x\& y=y\)。若 \(x\subset y\),稱 \(x\)\(y\) 的一個子集,\(y\)\(x\) 的一個超集。
給定陣列 \(f\),求陣列 \(g\)。其中 \(g_x=\sum_{y\subset x}{f_y}\)
\(f\) 中最大的數二進位制下共有 \(n\) 位。
如果直接列舉子集的話,時間複雜度為 \(O(3^n)\),可以最佳化。
不妨考慮如下演算法:

for(int i=0;i<n;i++){
    for(int j=0;j<1<<n;j++){
        if(!(j&1<<i)) f[j|1<<i]+=f[j];
    }
}

這個演算法把 \(n\) 位的二進位制數視作一個 \(n\) 維的每維為 \(0\)\(1\) 的向量,整個過程是在對整個 \(n\) 維空間做字首和。
具體一點,就是依次對於 \(0\sim n-1\) 維,把所有這一維是 \(0\) 的向量的字首和累到 \(1\) 上。
正確性:
\(x\subset y\),則在 \(x\)\(y\) 最高的不同的一維會算到 \(x\),在更低的位由於有高位不同不會算到。
\(x\not\subset y\),因為所有能算到 \(x\) 的向量都是將 \(x\) 的某些維從0變成 1 得到的(相當於能算到 \(x\) 的都是 \(x\) 的超集),
而至少存在某一維 \(x\)1\(y\)0,所以 \(y\)不可能算到x
時間複雜度\(O(2^nn)\),空間複雜度\(O(2^n)\)

例題:CF449D
給出 \(n\) 個數\(a_1,a_2,...,a_n\),求有多少個非空子集,其中所有數與起來等於零。
\(f_x\) 表示 \(x\) 出現次數,用剛講的高維字首和方法算出 \(g_x=\sum_{y\subset x}{f_y}\)
發現與起來為 \(x\) 的超集的子集個數很好算,即 \(2^{g_x}-1\)
設與起來恰為 \(x\) 的子集個數為 \(h_x\)。令\(s_x=2^{g_x}-1\),不難發現將 \(s\) 做高維字首和的逆變換即得到 \(h\),這個過程有點像容斥。

for(int i=0;i<n;i++){
    for(int j=0;j<1<<n;j++){
        if(j&1<<i) s[j^1<<i]-=s[j];
    }
}

答案即為 \(h_0\)

相關文章