算法 {二进制与&}
与&
性质
@MARK_2
;
对于数组A[1,2,3,...,N]
(每个元素是[0, 2^31]
范围), 令一个数组的价值为所有元素的与&运算的结果
; 那么所有以A[N]
为结尾的子数组(有N
个), 这些子数组 他们的价值 最多只有32
种可能;
.
即对于所有的i=[1-N]: | Set{ Val( A[i - N] ) } | <= 32
;
证明: 当i==N
时, 假设A[N]二进制有X个1
, &and: A[i...N]的&结果
, 那么随着你i --
往左移动, 此时的A[i-N]的结果
要么等于&and
要么是将&and
里的若干个1
给变成了0
; 即最初A[N]
有X个1
, 那么其实所有子数组的价值 最多只有X
个取值;
.
不要以为是X个1
的选与不选, 这是错误的; 比如当i==N
时 and = A[N] = 100110
, 然后当i == N-1
时 可能and = 100100
, 然后i == N-2
可能and = 000100
, 然后i == N-3
可能and = 000000
, 于是对于i <= N-3
其and == 000000
;
那么对于A[r]
,我们如何得到所有A[l]&...&A[r]
的值呢?(最多只有30个)
从cur = A[r]
开始,比如A[r] = 10010
即[1],[4]
位是1,因为只有遇到[1]/[4]
是0
的数 那么这个数cur
会变小, 因此我们计算[1]/[4]
位是0
的 且下标<r
的最大下标, 然后找最大值 比如[l]
(这说明A[l+1]&...&A[r] == A[r]
), 于是更新cur &= A[l]
;
vector<int> Ind[ 32];
int N = A.size();
FOR_( i, 0, N-1){
FOR_( bit, 0, 30){
if( (A[i]>>bit) & 1){}
else{
Ind[ bit].push_back( i);
}
}
}
int ANS = 1e9;
FOR_( i, 0, N-1){
for( auto cur = A[i], ind = i;;){
ANS = std::min( ANS, std::abs( cur - K));
int nex = -1;
FOR_( bit, 0, 30){
if( (cur>>bit) & 1){
auto it = std::lower_bound( Ind[bit].begin(), Ind[bit].end(), ind);
if( it == Ind[bit].begin()){ continue;}
it = std::prev( it);
nex = std::max( nex, *it);
}
}
if( nex == -1){ break;}
cur &= A[nex];
ind = nex;
}
}
return ANS;
.
例题: @LINK: https://leetcode.cn/problems/find-subarray-with-bitwise-and-closest-to-k/description/
;
@DELI;
-1 & x = x
;
因为-1 == 二进制全1
, 所以x
有1 结果是1, 有0 结果是0;
应用
@LINK: https://editor.csdn.net/md/?articleId=137481450
;
只要有0, 结果就是0;