算法 {二进制位运算}
@LOC_0
二进制位运算
定义
{取反~
(单目), 取负-
(单目), &, |, ^
(双目), >>, <<
(双目)}
−
x
-x
−x取负运算, 是完全只关注机器码的, 不会关注这个数的符号 或者用的什么编码; 也就是, 最高符号位(如果是有符号数) 与其他位 是同等对待的;
.
对于整数
a
a
a,
−
a
-a
−a(取负号) 就等价于 对
a
a
a的机器码进行补码操作(即取反加一);
.
比如a= [0000 0011], -a = [1111 1101]
;
~, &, |, ^
这几个运算, 是完全只关注机器码的, 不会关注这个数的符号 或者用的什么编码; 也就是, 最高符号位(如果是有符号数) 与其他位 是同等对待的;
.
比如, (负数) | (非负数)
他的结果 一定是负数 (因为最高位是1 | 0
);
而<<, >>
他分算术移位, 逻辑移位(全补0);
.
c++
使用的是算术移位, 即x (<<, >>) ?
: 如果x
是无符号的 则左右都补0
, 否则x
是有符号的 则右侧(即低位)补0
, 而左侧(高位) 补符号位 (即负数 高位补1
, 否则补0
);
性质
int a = 1<<31
, 虽然说 a
是有符号的, 但他的比特位 还是10...0
, 只不过 他的数值 是负数;
.
即二进制 他非常底层 与有无符号类型无关, 即int/uint x = 1<<31
他的比特位一定是10...0
;
@DELI;
a , − a a, -a a,−a的符号 可能是相同的;
当 a = M I N a = MIN a=MIN时, 此时进行补码操作后, 他的机器码 是不变的, 也就是 a = − a a = -a a=−a;
还有个情况, 即 a = 0 a = 0 a=0时, 此时 也存在 a = − a a = -a a=−a (即进行补码操作后, 两者的机器码是一样的);
@DELI;
对于一个二进制数, 如果存在两个1
他俩的距离为k
, 则称该数是非法的;
.
比如
k
=
1
k=1
k=1, 也就是不可以有 两个1 是相邻的;
可以通过if( (st << k) & st){ 非法}
;
.
证明: 假如有两个1 位置是a < b
且b - a = k
(这个数是非法的), 那么a
通过左移
k
k
k位以后, 他就到b
位置的, 此时两个1 都在b
位置, 就冲突了;
.
这是O(1)的 (你使用右移 即(st >> k) & st
也可以); 如果你遍历循环这个数 则是
O
(
N
)
O(N)
O(N)的;
@DELI;
向下取整;
a >> 1
操作, 根据补码原理, 得到: (不管a是正数负数, 结果都是: 向下取整)
这与a % 2
是不同的, 除法运算是向0取整; (比如: 5%2 -> 2; -5%2 -> -2
)
而a >> 1
会得到: (5>>1 -> 2; -5>>1 -> -3
);
@DELI;
*有符号整数x, 设其机器码为Bit[0,1,2,..., N-1(MSB最高符号位)], 若存在`ind`使得`Bit[ind] = 1`且`ind != MSB` (若有多个满足此的ind 则取最小的ind)*;
则`x & -x`的值 等于 `1 << ind`;
@DELI;
*有符号整数x, 设其机器码为Bit[0,1,2,..., N-1(MSB最高符号位)], 若存在`ind`使得`Bit[ind] = 0`且`ind != MSB` (若有多个满足此的ind 则取最小的ind)*;
则`(~x) & -(~x)`的值 等于 `1 << ind`;
算法
获取[0...R]
的bitsCount
之和| 获取[0...R]
里二进制位[bit]==1
的数的个数
//{
template< class _T_> _T_ ___Binary_CountOfBit( _T_ _rig, int _bit){ // [0,...,_rig]里 `二进制[bit]==1`的个数(比如`rig=5, bit=1` 答案为`2: 0b10,0b11`;
ASSERTsystem_( (std::is_same_v<_T_,unsigned int> || std::is_same_v<_T_,unsigned long long> || std::is_same_v<_T_,unsigned __int128>));
constexpr int BITS = sizeof(_T_)*8;
ASSERTsystem_( _bit>=0 && _bit<BITS, _bit, BITS);
if( _rig < (_T_(1) << _bit)){
return 0;
}
//>< `&rig = [10 ? 01], &bit = 2`
_T_ higCount = (_rig >> (_bit+1)); // `0b10`
_T_ lowCount = (_rig & ((_T_(1)<< _bit)-1)); // `0b01`
_T_ ANS = (_rig + 1);
ASSERTsystem_( _rig != _T_(-1)); // 否则`rig+1`就溢出了;
ANS -= (higCount * (_T_(1) << _bit)); // [(00/01) 0 (...)]
if( (_rig>> _bit) & 1){ // [10 0 (...)]
ANS -= (_T_(1) << _bit);
}
else{ // [10 0 (00|01)]
ANS -= (lowCount + 1);
}
return ANS;
}
template< class _T_> _T_ ___Binary_BitCountSum( _T_ _rig){ // [0,...,_rig]里 每个数的`bitCount`之和 (比如`rig=3` 答案为`0+1+1+2`);
ASSERTsystem_( (std::is_same_v<_T_,unsigned int> || std::is_same_v<_T_,unsigned long long> || std::is_same_v<_T_,unsigned __int128>));
constexpr int BITS = sizeof(_T_)*8;
_T_ ANS = 0; // @WARN: 用`_T_`可能会溢出;
for( int i = 0; i < BITS; ++i){ ANS += ___Binary_CountOfBit( _rig, i);}
return ANS;
}
直接计算[bit]==1
的个数 不好算, 我们计算 [bit]==0
的个数;
分为两种: {(小的) 0 (任意)
, 和 (最大) 0 (?)}
}, 第二种情况 此时这个?
也可能是任意
也可能是小的
, 这取决于rig[bit]
的值, 如果是1
则?=任意
, 否则 比如rig = ...0 x
那么此时? = [0,1,2,...,x]
;
遍历所有的1
for( auto t = mask; t != 0;){
auto bit = __builtin_ctz( t); t ^= (1<<bit);
//>< `mask[bit] == 1`;
}
@DELI;
这种方式, 效率比直接暴力(即FOR(bit,0,31){ if( (mask>>bit)&1) }
) 快1倍;