【杂项】位运算

抑或和

一个有意思的事情是这个样子的:

\(\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} = \binom{j}{i} \]

很好的结论。


事实上关于统计\(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];

来自\(\text{luogu P4317 花神的数论题}\)

posted @ 2022-06-30 11:39  bikuhiku  阅读(51)  评论(0编辑  收藏  举报