1. 程式人生 > >高維字首和學習小記

高維字首和學習小記

問題引入:

現有a[i](0<=i<2n)a[i](0<=i<2^n)
對於每一ii,求jia[j]\sum_{j∈i}a[j]
這裡的指二進位制狀態下的被包含。

暴力解決問題:

即暴力列舉子集求和。
複雜度計算:

i=0nCni2i\sum_{i=0}^nC_{n}^i*2^i

=i=0nCni2i1ni=\sum_{i=0}^nC_{n}^i*2^i*1^{n-i}

=(1+2)n=(1+2)^n

動態規劃:

考慮對於一個ii,列舉把一個0變成1,轉移上去。

但是這樣顯然會有重。

比如說i=0i=0
第一輪加上202^0,第二輪加上了212^1

也可以第一輪加上212^1,第二輪加上202^0

這樣就會重複。

那麼考慮使增加的1的位置遞增的。

fi,jf_{i,j}表示到ii了,上一次增加的1是第jj位上的。

那麼列舉k((k>=j)and(ik0))k((k>=j)~and~(i的第k位是0))f(i2k,k)+=f(i,j)f(i|2^k,k)+=f(i,j)

f(i,j)

這樣的複雜度是O(2nn2)O(2^n*n^2)

對於上面的式子可以字首和優化:
f(i,j)+=f(i,j1),f(i2j,j)+=f(i,j)(ij0)f(i,j)+=f(i,j-1),f(i|2^j,j)+=f(i,j)(i的第j位是0)

複雜度O(2nn)O(2^n*n)

正題:

把上述問題看作一個n維空間,每一維座標的範圍是[0..1][0..1],求字首和。

考慮二維暴力是怎麼做的(不差分)?

先一行一行的掃描,記錄下來和,然後再一列一列的掃描。

高維同理,就是一維一維的掃描。

下面給出程式碼實現:

fo(j, 0, N - 1) fo(i, 0, (1 << N) - 1)
		if(i >> j & 1) a[i] += a[i ^ (1 << j)];

複雜度O(2nn)O(2^n*n)