位运算
基本的算术位运算:
与 | 或 | 非 | 异或 |
and,& | or,| | not,~ | xor |
补码
32位无符号整数unsigned int:
直接把这32位编码C看作32位二进制数N。
32位有符号整数int:
以最高位为符号位,0表示非负数,1表示负数。
对于最高位为0的每种编码C,直接看作32位二进制数S。
若n为非负数,n的符号位(即最高位)为0,原码、反码、补码相同。
若n为负数,n的符号位(即最高位)为1,反码为原码除最高位取反,补码为反码加一。
移位运算
左移
1<<n=2,n<<1=2n
右移
n>>1=n/2
【例题】a^b(快速幂)
代码如下:
#include<bits/stdc++.h> #define ll long long using namespace std;
int main(){ int a,b,p; cin>>a>>b>>p; ll ans=1%p;
for(;b;b>>=1){ if(b&1)ans=(ll)ans*a%p; a=(ll)a*a%p; }
cout<<ans; return 0; }
方法一:类似于快速幂的思想
代码如下:
#include<bits/stdc++.h> #define ll long long using namespace std;
int main(){ ll a,b,p,ans; cin>>a>>b>>p;
for(;b;b>>=1){ if(b&1)ans=(ans+a)%p; a=(a+a)%p; }
cout<<ans;
return 0; }
方法二:long double做法
a*b mod p=a*b-(a*b/p)*p
代码如下:
#include<bits/stdc++.h> #define ll long long #define ld long double using namespace std;
int main(){ ll a,b,p,c,ans; cin>>a>>b>>p; a%=p;b%=p;
c=(ld)a*b/p; ans=a*b-c*p;
if(ans<0)ans+=p; else if(ans>=p)ans-=p;
cout<<ans; return 0; }
二进制状态压缩
操作 | 运算 |
取出整数n在二进制表示下的第k位 | (n>>k)&1 |
取出整数n在二进制表示下的第0~k-1位(后k位) | n&((1<<k)-1) |
把整数n在二进制表示下的第k位取反 | n xor (1<<k) |
对整数n在二进制表示下的第k位赋值为1 | n|(1<<k) |
对整数n在二进制表示下的第k位赋值为0 | n&(~(1<<k)) |
【例题】最短Hamilton路径(状态压缩DP)
使用一个n位的二进制数:若其第i(0≤i<n)位为1,则表示第i个点已经被经过,反之未被经过。
F[i,j](0≤i<2,0≤j<n):“点被经过的状态”对应的二进制数为i,且目前处于点j时的最短路径。
初始值:F[1,0]=0,F数组的其他值设为无穷大。
答案:F[(1<<n)-1,n-1]
状态转移方程:F[i,j]=min{F[i xor (1<<j),k]+weight(k,j)}
0≤k<n并且((i>>j)&1)=1
代码如下:
#include<bits/stdc++.h> using namespace std;
int n; int f[1<<20][20]; int w[20][20];
int main(){ cin>>n; memset(f,0x3f,sizeof f); f[1][0]=0;
for(int i=0;i<n;i++) for(int j=0;j<n;j++) cin>>w[i][j];
for(int i=1;i<1<<n;i++) for(int j=0;j<n;j++)if(i>>j&1) for(int k=0;k<n;k++)if(i>>k&1) f[i][j]=min(f[i][j],f[i^1<<j][k]+w[k][j]);
cout<<f[(1<<n)-1][n-1]; return 0; }
运算符优先级从高到低的顺序:
加减 | 移位 | 比较大小 | 位与 | 异或 | 位或 |
+,- | <<,>> | >,<,==,!= | & | xor | | |
成对变换
对于非负整数n:
当n为偶数时,n xor 1等于n+1
当n为奇数时,n xor 1等于n-1
“0与1”、“2与3”、“4与5”……关于xor 1运算构成“成对变换”。
lowbit运算
lowbit(n)取出非负整数n在二进制表示下最低位的1以及它后边的0构成的数值。
lowbit(n)=n&(~n+1)=n&(-n)
作用:
①配合Hash可以找出整数二进制表示下所有是1的位,所花费的时间与1的个数统计。
②是树状数组中的一个基本运算。
const 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>0){ cout<<H[n&-n]<<‘ ’; n=n&-n; } }