0x01 位运算

0x01 位运算

在c++中,8位二进制数代表char类型,范围为-128~127,其中0xFF代表-1,0x7F代表最大值127。

另外,m位的二进制数,为方便起见,最低位称为第0位,从右往左依次类推,最高位为m-1位

1.补码

32位无符号整数 unsigned int:

直接把这32位编码C看做32位二进制数N

32位有符号整数 int:

以最高为符号位,0表示非负数,1表示负数。

对于最高位为0的每种编码C,直接看做32位二进制数S

同时,定义该编码按位取反后得到的编码~C的数值为-1-S

运算发生算术溢出时,32位无符号整数相当于自动对2^32取模有符号整数算术溢出时可能会出现负数

补码也被称作”二补数“。还有一种编码称作反码,也叫”一补数“,直接把C的每一位取反表示负C。补码和反码在负数表示中,绝对值相差1。

32位补码int(十进制)int(十六进制)
0000...000000x0
0111...111121 4748 36470x7F FF FF FF
00111111重复四次10 6110 95670x3F 3F 3F 3F
1111...1111-10xFF FF FF FF

上述表中的0x3F 3F 3F 3F 是一个非常有用的数值,它是满足以下两个条件的最大整数。

1.整数的两倍不超过0x7F FF FF FF,即int表示的最大整数。

2.整数的每8位(每个字节)都是相同的。

我们常用memset(a,val,sizeof(a))来初始化一个int数组a,该语句把数值val(0x00~0xFF)填充到数组a的每个字节上,而一个int占用4的字节,所以memset只能赋值出“每8位都相同”的int。为了避免算术上溢,我们通常用memset(a,0x3f,sizeof(a))给数组赋值0x3F 3F 3F 3F

一般设置无穷大为0x3F 3F 3F 3F,设置无穷小为0xcF cF cF cF

2.移位运算

左移

在二进制表示下把数字同时往左移动,低位以0填充,高位越界后舍弃。
1 < < n = 2 n , n < < 1 = 2 n 1<<n=2^n, n<<1=2n 1<<n=2n,n<<1=2n
右移

这里的右移特指算术右移

在二进制补码表示下把数字同时向右移动,高位以符号位填充,低位越界后舍弃。
n > > 1 = ⌊ n 2.0 ⌋ n>>1=\lfloor \frac{n}{2.0} \rfloor n>>1=2.0n
算术右移等于除以2向下取整,(-3)>>1=-23>>1=1

(-3)/2=-1,3/2=1。除法和算术右移有区别,注意区分。

快速幂
计算 a b m o d p 计算a^b mod p 计算abmodp

long long quickpow(long long a,long long b,long long p)
{
    a%=p;
    long long ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%p;
        a=a*a%p;
        b>>=1;
	}
    return ans%p;
}

时间复杂度O(logb)

ab不能超过long long范围9e18

3.二进制状态压缩

二进制状态压缩是指一个长度为m的bool数组可以用一个m位二进制数来表示并储存的方法。利用下列位运算的方法可以实现对原bool数组中对应下标元素的存取。

操作运算
取出整数n在二进制表示下的第k(n>>k)&1
取出整数n在二进制表示下的第0~k-1位(后k位)n&((1<<k)-1)
把整数n在二进制下的第k位取反n^(1<<k)
对整数n在二进制表示下的第k位赋值1n|(1<<k)
对整数n在二进制表示下的第k位赋值0n&(~(1<<k))

这种方法运算简便,并且节省了程序运行时间和空间。当m不太大的时候,可以直接使用一个整数类型来储存。当m较大时,可以使用若干个整数类型(int数组)来储存,也可以直接利用C++STL提供的bitset功能实现(第0x71节)。

4.成对变换

通过计算可以发现,对于非负整数n:

n为偶数时,n xor 1等于n+1

n为奇数时,n xor 1等于n-1

因此,“0和1”,“2和3”,“4和5”…关于xor 1运算构成成对变换。

应用:

常用于图论邻接表中边集的储存。在具有无向边(双向边)的图中把一对正反方向的边分别储存在邻接表数组的第n和n+1的位置(其中n为偶数),就可以通过xor 1的运算获得与当前边(x,y)反向的边(y,x)的储存位置。详细在邻接表(第0x13节)。

5.lowbit运算

lowbit(n)定义为非负整数n在二进制表示下“最低位的1及后边所有的0”构成的数值。

lowbit(n) = n&(~n+1) = n&(-n)

lowbit运算配合Hash可以找出整数二进制下所有是1的位,所花费的时间与1的个数同级。

为了到达这一目的可以不断个给n赋值为n-lowbit(n),直到n=0。例如n=9=1001。lowbit(9)=19-lowbit(9)=8=1000lowbit(8)=88-lowbit(8)=0。取1和8的对数 l o g 2 1 log_2 1 log21 l o g 2 8 log_28 log28,可知是n的第0位和第3位。但c++的log函数以e为底,且时间复杂度大,所以我们预处理一个数组,通过Hash来代替log运算。

当n较小时,最简单的方法是建立一个数组H,令H[ 2 k 2^k 2k]=k

const int MAX_N = 1 << 20;
int H[MAX_N + 1];
for (int i = 0; i <= 20; ++i)
    H[1 << i] = i;
while (cin >> n)
{
    while (n)
    {
        cout << H[n & -n] << ' ';
        n -= n & -n;
    }
    cout << endl;
}

稍微复杂但效率更高的方法是建立一个长度为37的数组H,令H[ 2 k m o d 37 2^kmod37 2kmod37]=k。

这里利用了一个小技巧: ∀ k ∈ [ 0 , 35 ] \forall k\in[0,35] k[0,35] 2 k m o d 37 2^kmod37 2kmod37互不相等,且恰好取遍整数1~36。修改后的程序:

int H[37];
for (int i = 0; i < 36; ++i)
    H[(1ll << i) % 37] = i;
while (cin >> n)
{
    while (n)
    {
        cout << H[(n & -n) % 37] << ' ';
        n -= n & -n;
    }
    cout << endl;
}
  • 29
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谷神星ceres

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值