本文旨在通過兩道巧妙運用位運算的題,認識位運算的魅力
題意:目前求任意兩序列的最長公共子串行,是沒有複雜度低於\(o(|a|\cdot |b|)\)的演算法的給定兩個序列\(a,b\),求\(a,b\)的最長公共子串行
\(|a|,|b|\le 10^5\)
時限:\(5s\)
回顧經典的\(o(|a|\cdot |b|)\)
\[f_=max(f_,f_,f_+[a_i+b_j])
\]顯然有如下性質:
\[\begin
&f_\le f_\\
&f_\le f_\\
&f_-f_\le 1\\
&f_-f_\le 1
\end\]
令\(m_=f_-f_\)
考慮轉移
我們將\(\text_\)劃分為若干段,每段以乙個\(1\)結尾:
[000...01][000...01][000...0]
^^ ^
12 |b|
考慮\(a_i\)出現在\(b\)中的位置集合,令其為\(\text\)
令\(\text_|\text\)
對於\(\text_\)的每一段,保留\(\text\)最前面的乙個\(1\),則為\(\text_i\)
例如
$m_$=[000...01][000...01][000...0]
$ab$=[110...00][000...00][001...0]
$m_i$=[100...00][000...01][001...0]
我們已知了轉移方式,考慮如何通過位運算得到
對於某一段如:x=[0011001]
考慮前導0變成1,第乙個1變成0:[1101001]
異或上x,得到[1110000]
在按位與上x,得到[0010000]
我們將\(\text\)翻轉,第一步操作變成
x=[1001100]
[1001011]
這個意義是\(-1\),這是一段的操作,對所有段如何快速進行呢
發現我們將\(\text_\)整體翻轉後
再左移,給最右邊填上\(1\),效果為:
$m_$=[000...0][100...0][100...0]
^ ^^
|b| 21
[000...1][000...1][000...1]
這恰好使得每一段都變成1
那麼直接減即可,具體細節見**
將矩陣的行壓成\(k\)塊,即可實現\(o(|a|\cdot k)\)的時間複雜度
以下**在\(n,m=10^5\),不開任何優化的情況下,跑進1s
#includetypedef int ll;
typedef unsigned long long ull;
const ll maxn=1e5+9;
ll read()
while(c>='0' && c<='9')return x*f;
}ll n,m,ans;
ll a[maxn],b[maxn];
ull dp[maxn/63],pos[maxn][maxn/63];
int main()\)為\(\\)的某個排列)
每個點還有乙個顏色\(c_i\)
\(q\)次詢問,每次給定\(l,r,d,u\),求集合\(\\)的顏色個數
\(n,q\le 10^5\)
令\(a_\)為第\(i\)次詢問是否包含顏色\(c\),考慮得到其
顯然可以將\(i\in[l,r],p_i\in[d,u]\)可以拆開矩陣\(a\)顯然為01矩陣,需要將各行按列相加,得到答案令\(x_\)為第\(j\)次詢問是否滿足\(l_j\le i\le r_j\),我們對於\((l_j,j)(r_j+1,j)\)進行掃瞄線,能很輕鬆的得到
令\(y_\)為第\(j\)次詢問是否滿足\(d_j\le i\le u_j\),同理可得
那麼\((x_i\and y_j)_j\)即為點\(i\)是否被包含在第\(j\)次詢問中
現在可以很簡單得到\(a\)
對於每列,這裡我們如此維護其答案:
令\(cnt_i\)為第\(i\)列目前的答案那麼我們分治,對於顏色\(\in[l,mid]\),令其矩陣為\(c_0\),對於顏色\(\in(mid,r]\),令其矩陣為\(c_1\)\(cnt_i=\sum\limits_^ 2^k\cdot c_\)(其中\(c\)為01矩陣)
假設這裡\(cnt_i\le n\),那麼改寫成
\(cnt_i=\sum\limits_^ 2^k\cdot c_\)
令\(logn\)為矩陣的行數,稱為\(h\)
考慮合併\(c_0,c_1\):\(c'=c_0+c_1\),假設\(c_0,c_1\)的\(h_0,h_1\)均為\(l\),為了方便,則矩陣\(c\)的\(h=l+1\)
合併的過程,相當於是二進位制高精,考慮進製,很容易實現
分析其時間複雜度
令顏色種類為\(n\),令\(k\)為將矩陣\(c\)的行壓成的塊數
\(t(n)=2t(n/2)+klogn\),\(k\)為常數,提取出來
\(t(n)=2t(n/2)+logn\),運用主定理得到\(t(n)=n\)
時間複雜度為\(o(k\cdot n)\)
巧妙運用 按位或「 」 運算
今天在學習collections的原始碼時,看到下面的 聯絡之前自己寫的程式,感受到下面這段 確實巧妙 1 public static boolean addall collectionc,t.elements 下面簡單介紹 t.elements 可變引數列表,元素為泛型t或t的子類。這是類似於 這...
位運算的巧妙應用
與運算 或運算 異或運算 非運算 移位運算 和 題目要求 用乙個表示式,判斷乙個數x是否是2的n次方,即2,4,8,16 等,要求不可以用迴圈語句。解析 2,4,8,16這樣的數轉化成二進位制是10,100,1000,10000。如果x減去1後 低一位並且二進位制的每一位都是1 這個數與x做與運算,...
位運算的巧妙設計
位運算子 例子名稱 結果 a b and 按位與 將把 a 和 b 中都為 1 的位設為 1。a b or 按位或 將把 a 和 b 中任何乙個為 1 的位設為 1。a b xor 按位異或 將把 a 和 b 中乙個為 1 另乙個為 0 的位設為 1。a not 按位取反 將 a 中為 0 的位設為...