• OIer应该知道的二进制知识


    计算机使用(2)进制,这是众所周知的。在学习(OI)的过程中,(2)进制也显得尤为重要。有时候,细节决定成败,所以我想总结一下容易被遗忘和误解的关于(2)进制的知识。

    1、运算符

    &:与。1&1=1,1&0=0,0&0=0;(同真为真)

    |:或。1|1=1,1|0=1,0|0=0;(一真俱真)

    ^:异或。1 ^ 1=0,1 ^ 0=1, 0 ^ 0=0;(阴阳协调为好,极阴或极阳皆为坏)

    num>>len:将num左移len位,低位溢出舍弃,高位不足补0。比如1010>>3就等于0001

    num<<len:将num右移len位,高位溢出舍弃,低位不足补0。比如1010<<3就等于0000

    ~:全位取反。比如 ~ 1010=0101

    位运算优先级要比加减乘除等运算优先级低,所以用的时候记得加括号。

    2、原码,补码,反码

    对于这三个东西我并不是很清楚他们分别是什么意思,在(OI)中,我觉得记住下面几点就可以了。

    二进制数的第一位表示符号,(0)为整数,(1)为负数。(int)作为一个(32)位整形数据类型可以保存([-2^{31}-1,2^{31}-1])的所有整数。因为第一位存符号去了,所以只是到(2^{31})而不是(2^{32})(unsigned) (int)不保存符号位就可以到(2^{32})(long) (long)同理。所谓溢出就是数值超过(32)位二进制数能存的范围((64)位同理,我们以(int)为例)。比如(01111111111111111111111111111111)(十进制下是(2147483647))也就是(int)能保存的最大的数字。加上一个(1)之后理应是(2147483648),二进制下就是(10000000000000000000000000000000)。但是因为第一位是符号位,所以就变成负数了。而每个数逐位取反都是自己的相反数(-1),所以(~2147483647=-2147483648)。再举几个例子:

    比如(-1(11111111111111111111111111111111))取反之后就是(0(000000000000000000000000))。所以我们也可以由这一点快速求出一个数的相反数,就是逐位取反之后再(+1),也就是(~num+1=-num)

    比如~(1(00000000000000000000000000000001)+1=-1(11111111111111111111111111111111))

    3、lowbit

    (lowbit(i))表示(i)在二进制下从低位到高位第一个(1)与它后边跟着的(0)是多大。比如(lowbit(24[11000])=8[1000])

    显然,我们会一种(O(logn))的求法求(lowbit(i))。但是运用上面的知识,我们可以(O(1))(lowbit(i))

    假设某个数二进制表示下后面一段是(1000..000),前面我们不管。把它逐位取反就变成了前面一段取反加上(011111...111)。这个时候两个数&起来是等于(0)的,因为没有哪个位置同为(1)。假设我们把取反之后的数字(+1)。那么就会变成原数前面一段取反加(100....000)。这个时候&起来,前面是反的,全部都会变成(0),刚刚好(lowbit(i))所求的部分会被保存下来,所以(lowbit(i)=i)&((()~(i))+1())。也就是(lowbit(i)=i)&((-i))

    4、memset

    在很久以前我就疑惑过,为啥(int)类型占(4)字节,(long) (long)(8)字节。我们可以灵性的理解一波,在二进制下,每八位占一个字节。而(memset),可以将某个数的每八位全部赋值成一样的数字。比如(memset(a,1,sizeof(a)))。就是把(a)数组里的每一个值都赋值成(00000001000000010000000100000001)(memset(a,255,sizeof(a)))就是把(a)数组里的每一个数值都赋值成(111111111111111111111111)(0x7F),是一个十六进制数,表示(16^1*7+16^0*15=127)(0x)放在前面起声明这是个十六进制数的作用。由于(2)位十六进制数相当于(8)位二进制数,所以我们一般在(memset)里写两位十六进制数。(memset(a,0x7F,sizeof(a)))是将(a)数组全部赋值成为(01111111011111110111111101111111),这是(memset)能赋值出的最大的有符号类型整数。(0x3F)=(16^1*3+16^0*15=63),相对于(0x7F),这个数字更加不容易加爆,所以后面涉及加法的情况下,我们还是有(memset(a,0x3f,sizeof(a)))比较好。

  • 相关阅读:
    次小生成树
    乘法逆元(递推)
    乘法逆元(快速幂)
    带偏移量的并查集
    Tarjan 强连通分量
    Luogu_P2461 [SDOI2008]递归数列 【题解】 矩阵乘法
    Luogu_P2243 电路维修【题解】 双端队列bfs
    Luogu_ P2962 [USACO09NOV] 灯 【题解】 双向搜索
    luogu_P2044【题解】 随机数生成器 矩阵乘法
    luogu_P2054 bzoj 1965 洗牌 【题解】 快速幂 快速乘
  • 原文地址:https://www.cnblogs.com/AKMer/p/9698694.html
Copyright © 2020-2023  润新知