考慮如下問題:
記 \(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\)。