0x01

本文围绕位运算展开,介绍了位运算的运算符,如与、或、非、异或、左移、右移等,还讲解了补码、16进制声明符号0x。阐述了位运算在DFS标记回溯、状态压缩DP、快速幂等场景的应用,能降低时空效率和编程复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      现在立个FLAG吧,三个月读完《算法竞赛进阶指南》这本书。。。作者:李煜东。。。出版社:河南电子音像出版社??(什么 鬼  来的。。。)这是我的第一篇关于算竞进(简称,我超级喜欢这么叫)文章,就跟着书走吧。。。

                     0x00基本算法  -  0x01位运算

       说实话其实之前在刚入门OI的时候就已经接触过位运算了,特别是在之前学状压DP的时候,细心地研究了一下。。。所以这一章算学得很轻松。。。所谓位运算,就是在位上进行运算(在二进制的每一位上进行操作),这种运算有什么优势呢,,它可以降低时空效率,降低编程复杂度,,至于体现在哪?后面会谈到。。。

       先介绍几个微操作的运算符吧。与(&C++表示符)读作and ,或 (|)读作or  ,非 (~) 读作not ,异或 (^)读作xor(PS:因为在C++的异或和数学的乘方一样,所以统一把书面的异或写作xor),左移(<<)右移(>>)别把这个和输入输出的搞错了。。。

          补码是什么?这个是C++的独特码,32位无符号整数的补码就是它的二进制数,无符号就是都是非负数(没有符号嘛)。。。32位有符号整数最高位是表示数的正负,0为正数,1为负数。。。int 的数据范围就是-2^31~0~ 2^31-1而unsigned int 就是0~2^32-1(这个很好理解吧,其实int也是32位但是因为它有一位当作区别正负的了,所以最大值就比unsigned int小了)

当发生溢出的时候会自动省略32位后的东西例如发生一些加减之后,第33位是1,然而这个1没有什么luan用,它会自动过滤掉。。只取后面的数得到对应的数值。。。

          然后我们介绍一下这个0x是什么鬼玩意儿来的。。。0x表示16进制的声明符号,十六进制啊到10的时候就开始用ABCDEF分别代替10到15,这有什么优势呢?因为16进制下的每个数刚好占四位,32%4==0这样就很方便对数组数进行赋值。。。memset就是经过这个原理赋值,所以有些同学发现,这个memset可以赋值0,-1但是赋值1就变成一个很大的数了。这个有必要去深究,,有空研究一下,可以用来考(zhuang)试(bi)。。。

           然后就开始讲位运算(书上没有详细讲,那我就详细讲讲)。。。&这个东西啊,就是and我们平时的逻辑运算符差不多&&,含义也是差不多的意思是“同时”,也就是二进制对应的位只有同时为1才是1其他都是0  比如a&b把a转化成二进制是11011

b转化成二进制是01010   那么两个在一起就是这个样子的啦。。。&呢通常用来判断属于不属于比如上面的a&b如果我们要看b所有的元素是不是都在a中就可以这样子写 if(a&b==b) 如果b有一位是1然而对应的a的那一位是0 那么返回来的那一位就是0,再转化成十进制就绝对不是b了。然后就是|这个鬼东西。。。它的意思也是和||差不多的,就是或者,也就是对应的二进制位中只要有一个是1那么结果就是1。再拿刚才的a&b做例子那么结果就应该是这个样子的了嗯。这么快就讲完了&和|其实这两个在高中一年级的数学中也有对应的含义,&就是交集 |就是并集。。这个有兴趣就去了解一下吧。。。那^这个半个笑脸的东西又有什么用呢?相同为0不同为1我们又拿出我们的千年老陈醋(呸,老例子a&b)然后就是~取反,其实很好理解的,就是对应的每一位都取反0变1,1变0 最后就是压轴出场的<<和>>这两姐妹。。<<叫左移就是把对应的二进制位向左移一位后面用0补上,例如1<<2,1的二进制是0001那么左移2就变成0100了,同理右移就是向右移不管它最后一位是0是1都给抹杀掉。1>>1就是0001变成0000。这些都是很神奇的操作,但是我们好像还是不知道到底有什么luan用呢?继续看。。。

          在我们DFS的时候不是要对每个元素进行标记回溯吗。如果元素总量小的话就可以用一个数来表示集合。。这个数的二进制对应每一个元素,1表示已经选了,0表示没有选。。这样子就不用你费事地循环去找哪些选了那些没选看这个数是否选了就是   set&(1<<i)i是这个元素的位置,set是现在选了的数组装而成的一个集合。如果返回不是0那么就是set第i位这个位置上有1。如果要更改某一位的数可以这样n|(1<<i)第i位变成1 ,n&(~(1<<k))第i位变成0等等神奇的操作,这种东西在状态压缩DP的时候可以有大用处的。。。

           其实说了这么多还是拿出快速幂的例子来看看。。。都知道快速幂吧,,,a^b%mod a的b次方%mod,虽然我们有超神的公式(a*b)%mod==a%mod*b%mod

这样只是保证你不爆Int而已不能保证不超时,如果b很大呢?例如b>1亿这样做就会超时了啊,这个时候就要看我们的神奇性质了,一个二进制数对应的十进制数其实是这样的例如1011就对应 1*2^0+1*2^1+0*2^2+1*2^3。。这个应该秒懂啊。。。如果a的2(1011)次方的话不就是a^(1*2^0+1*2^1+0*2^2+1*2^3)吗,然后再用数学分解就变成a^(1*2^0)  *  a^(1*2^1)   *   a^(0*2^2)   *a^(1*2^3)  这个是小学数学吧??嗯。。就是这样,因为一个int最多只有32位,所以b最多只操作32次,就是这样啦通过一位去寻找每一位是否是1然后进行运算。。。二话不说,,上代码

int quickly_power (int a,int b,int mod) //a^b%mod
{
    int ans=1;
    while(b>0)
    {
        if(b&1) ans=(long long)ans*a%mod;//最好写个强制类型转换,防止两个int乘爆了
        a=(long long)a*a%mod;
        b>>=1;
    }
    return ans;
}

          其实还有一些很神奇的地方有用,比如建双向边的时候啊,0^1=1  ,1^1=0一个^就可以方便地找到它的另外一条逆向边。。。还有lowbit啊树状数组必备神器。。。那位运算就讲到这么多了。。。再见。。(送福利 啊,小朋友好不容易才熬到这里哦嘻嘻嘻……)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值