二进制的知识总结
提到二进制,莫过于二进制的运算或者应用,立一个flag总结知道的所有二进制知识,二进制位第一位为第0位!
注:在oi之中所有的log运算都是以2为底的!
二进制运算符:y<<x,左移,y的二进制数左移x位,相当于y乘以2的x次方,eg:4<<2=16;
y>>x,右移,y的二进制数右移x位,相当于y除以2的x次方,但是这里>>1和除法有一点点的不同;ex:2>>1=1;2/2=1;这里是相同的。
但是-3>>1=-2;而-3/2=-1;负数的二进制如果是奇数也是向下取整。
y|x,按位或,y的二进制数和x的二进制数全部进行二进制或的运算。法则:0|0=0 0|1=1 1|1=1 1|0=1;注意二进制位只有0和1.
y&x,按位与,y的二进制数和x的二进制数全部进行二进制与的运算。法则:0&1=0 1&0=1 1&1=1 0&0=0;这里有一个二进制的用法,可以用这个符号来判断奇偶,因%实在是太慢了,奇偶的判断if(x&1==1)x是一个奇数,if(x&1==0)x是一个偶数。
y^x,按位异或,y的二进制数和x的二进制数全部进行二进制异或的运算。法则:0^1=0;1^0=1 0^0=0 1^1=1;这个操作经常是当有滚动数组的时候滚动时^1即可实现01之间的转换。
~x,按位取反,x的二进制数每一位的数如果是0,变成1.是1变成0;但是这个取反操作是针对当前x的二进制数(注意x的二进制数不够32位的话要前端全部补0)然后进行取反操作这时并不是~8=7了,而是~8=-9,为什么会这样呢下面和反码与补码有关。法则:~x=-1-x;
在计算机中,负数以其正值的补码形式表达。什么叫补码呢?这得从原码和反码说起了:
原码:一个整数,按照绝对值大小转换成的二进制数,称为原码。如1:原码:00000000 00000000 00000000 00000001
反码:将二进制数按位取反,所得的新二进制数称为原二进制数的反码。取反操作指:原为1,得0;原为0,得1。(1变0; 0变1)
如1:反码:11111111 11111111 11111111 11111110;
补码:反码加1称为补码。
如1:补码:11111111 11111111 11111111 11111111;32个1,这就是-1的二进制了。
于是很显然这里补码等于反码+1,而补码是当前数字的负值,所以反码就等于当前数字的负值-1.也就是这个~x+1=-x;~x=-1-x;
二进制的应用:快速幂:利用二进制的拆分原理进行快速求幂
int fast(int w,int y)//这里求w的y次方 { int w=b,temp=1; while(y!=0) { if(y&1==1)temp*=w; y>>=1;w*=w; } return temp; }
多重背包问题时也使用了二进制的拆分,首先当此件物品的体积乘上它的数量如果大于背包的容积的话直接完全背包即可,但是如果不大于的话就要考虑当前背包到底取多少件物品才算是最优呢?这里使用二进制的拆分可以得到任意数量的结果再进行01背包最后取出二进制拆分后余留下来的数再进行01背包这样就可与保证每种方案都可以被遍历一遍。
//多重背包的二进制拆分。
int k=1,ans=b[i]; while(k<=ans) { for(int j=m;j>=k*w[i];j--) f[j]=max(f[j],f[j-k*w[i]]+k*y[i]); ans-=k;k+=k; } for(int j=m;j>=ans*w[i];j--) f[j]=max(f[j],f[j-ans*w[i]]+ans*y[i]);
找lca的时候一个一个往上跳也很浪费时间所以这里考虑采用倍增的方法表示往上跳了任意的数目。
好久没打lca了,以后再更新。
lowbit运算:找到整数二进制表示下最低位的1及其以后边所有的0所构成的数值;ex:lowbit(8)=8,lowbit(9)=1;
设n>0,n的第k位是1,第0~k-1位都是0.首先先把n取反,此时第k位变成0,第0~k-1位都是1,再令n++;,此时因为进位,第k位变成为1,第0~k-1位都是0,所以n&(~n+1)可以得出最低位1及其后边所有的0所构成的数值,因为补码=反码+1=当前的数字的负值,所以可以得到如下式子。
~n+1=-n,~n=-1-n;此时n&(~n+1)=n&(-1-n+1)=n&(-n);
int lowbit(int n)//表示最低位的1及其以后边所有的0; { return n&(-n); }
lowbit+hash则可以找出整数二进制表示下所有是1的位,所花费的时间与1的个数同级。
for(int i=0;i<=20;i++)ha[1<<i]=i; while(scanf("%d",&n)==1) { while(n>0) { printf("%d ",ha[lowbit(n)]); n-=lowbit(n); } }
利用这个lowbit运算就可以把一个整数x计算出[1,x]分成的O(logx)个小区间。
while(x>0) { printf("[%d,%d]\n",x-(x&-x)+1,x); x-=x&-x; }
时间并不会因为你的迷茫和迟疑而停留,就在你迷茫的同时,不知道有多少人在冥思苦想,在为算法废寝忘食,不知道有多少人在狂热地拍着代码,不知道又有多少提交一遍又一遍地刷新着OJ的status页面……