【杂项】位运算
抑或和
一个有意思的事情是这个样子的:
设 \(\operatorname{XOR}_{i=1}^{n}i=1\operatorname{xor}2\operatorname{xor}\cdots\operatorname{xor}n\)
则 \(\operatorname{XOR}_{i=1}^{n} = \begin{cases} 1&n\equiv 1\pmod 4\\ n+1&n\equiv 2\pmod 4\\ 0&n\equiv 3\pmod 4\\ n&n\equiv 0\pmod 4\end{cases}\)
那么我们就可以愉快的快速求抑或和了。
template<typename Tec>
Tec XOR_SUM(Tec _aim) {
switch(_aim%4) {
case 1 :
return 1;
case 2 :
return _aim+1;
case 3 :
return 0;
case 0 :
return _aim;
}
}
& and | and ^
\(a\operatorname{xor}b=(a\operatorname{and}b)\operatorname{xor}(a\operatorname{or}b)\)
\(a\operatorname{and}(-a)=\operatorname{lowbit}(a)\)
\(a\operatorname{or}(-a)=-\operatorname{lowbit}(a)\)
\(a\operatorname{xor}(-a)=-\operatorname{lowbit}(a)<<1\)
lowbit and highbit
关于\(\operatorname{lowbit}\),大家都知道\(\operatorname{lowbit}(a) = a\operatorname{and}(-a)\)
template<typename Tec>
Tec lowbit(Tec _aim) {
return _aim&(-_aim);
}
不过关于\(\operatorname{highbit}\),我没啥好求法......
int BINtable[256];
void BINmake() {
BINtable[1] = 1;
for(int i = 1;i <= 8;++i)
for(int j = (1<<(i-1));j < (1<<i);++j)
BINtable[j] = i;
}
template<typename Tec>
Tec highbit(Tec _aim) {
if(!BINtable[1])
BINmake();
int BINcnt = 0;
Tec BINtmp = _aim;
while(BINtmp) {
BINtmp >>= 8;
BINcnt++;
} BINcnt--;
_aim >>= 8*BINcnt;
return (Tec)1<<((Tec)8*BINcnt+BINtable[_aim]-1);
}
好了,我找到了\(operatorname{O}(1)\)的科技:
int Log2(double x) {
if(x == 1) return 0;
return ((*(ull*)&x>>52)&1023)+1;
}//highbit;
离谱就。
不过您也可以用__buildin
函数来算,一定更快。(而且比赛的GNU
还是支持这东西的,虽然这不是C++
标准,而是GNU
的私货)
与和 或和
若记\(j = \operatorname{highbit}(k\operatorname{xor}l)\)
\(\large\operatorname{AND}_{i = k}^{l}i = (\operatorname{not}((j<<1)-1))\operatorname{and}k\operatorname{and}l\)
其实是这个样子的:
我们发现一个数\(n\)的第\(k\)位是\(1\),当且仅当\((n<<k) = 1\),
所以因为是与嘛,就要保证中间没有第\(k\)位是\(0\)的,
因此\(\operatorname{AND}_{i = k}^{l}i\)的第\(i\)位是\(1\),当且仅当\((k<<i) = 1\)且\((k<<i) = (l<<i)\),
即满足两个条件:
1.前面和现在所有位都相等;
2.现在这一位是1。
易得。
template<typename Tec>
Tec AND(Tec _b,Tec _e) {
return (~((highbit(_b^_e)<<1)-1))&_b&_e;
}
那么或和也易得了:
\(\large\operatorname{OR}_{i=k}^{l}i=\operatorname{not}\operatorname{AND}_{i=\operatorname{not}k}^{\operatorname{not}l}i\)
template<typename Tec>
Tec OR(Tec _b,Tec _e) {
return ~AND(~_b,~_e);
}
统计二进制下有一定位数的1的数字个数
事实上,将\(1\sim 2^k-1\)与\(2^k+1 \sim 2^{k+1}-1\)相比,会发现后者总是每一个数字的\(1\)的位数比前者多\(1\)。
于是我们可以这样解决统计\(1\sim 2^k\)中1的位数为\(x\)的个数的问题:
把问题递归成\(1\sim 2^{k-1}\),统计完之后,再利用上面的结论统计\(2^{k-1}+1\sim 2^{k}-1\),然后中间夹了个\(2^k\)。
实际上,设\(dp_{i,j}\)为\(1\sim 2^i -1\)中1的位数为\(j\)的数字个数,则有:
很好的结论。
事实上关于统计\(dp_{i,j}^{'}\)(\(1\sim i\)中有\(j\)位为\(1\)的数字个数),我们有更好的办法:
for(int i = 50;~i;--i) {
for(int j = 50;j;--j)
final[j] += final[j-1];
if(n>>i&1)
++final[cnt++];
}
++final[cnt];