位运算
位运算
基本的算术位运算:
与 |
或 |
非 |
异或 |
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=2n,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<2n,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;
}
}