1. 补码
由于CPU在进行设计的时候只进行了加法器的设计,所以在它只会处理加法操作,那么问题来了如何进行减法操作呢?例如:A-B,我们可以将其看作是A+(-B)的形式。这样在计算机内部就会出现负数的形式,所以就引入了补码的概念,补码就是对数字进行按位取反再加一的操作,补码在计算机中进行储存负数。
2. 移位运算
1. 左移
就是将二进制中的bit位向左移动一位,低位用0进行填充,高位越界后舍弃。
2. 右移
1. 算数右移
就是将二进制中的bit位向右移动一位,高位用符号位进行填充,低位越界后舍弃。值得注意的一点-3>>1=-2, -3/2 = -1,算数右移按照向下取证的观点,除法运算按照向0取证的观点。
2. 逻辑右移
就是将二进制中的bit位向右移动一位,高位用0进行填充,低位越界后舍弃。
3. 二进制状态压缩
将一个长度为m的bool数组用二进制整数的每一bit位来表示并存储的方法。
常用操作:
1. 取出n的第k位bit的数值:n>>k & 1
2. 取出n的0-k位的数值:n & ((1 << k) -1)
3. 将n的第k位进行取反操作:n ^ (1 << k)
4. 将n的第k位赋值成1:n | (1 << k)
5. 将n的第k位赋值成0:n & (~(1<<k))
4. 成对交换
我们对n进行n^1的操作时发现,如果n为奇数这个操作可以转化成与他相邻的偶数,n ^ 1 ^ 1这样可以得到n本身,这样的数字交换称为成对交换。
5. lowbit运算
lowbit(n)表示非负数的最后一位1表示的数值是多少
inline int lowbit(int n){ return n & -n; }
相关练习:
1. 0101 a^b
#include<iostream> using namespace std; int qmi(int a, int b, int p){ int t = 1; while(b){ if(b&1) t = 1ll * t * a % p; a = 1ll * a * a % p; b >>= 1; } return t % p; } int main(){ int a,b,p; cin>>a>>b>>p; cout<<qmi(a,b,p)<<endl; return 0; }
2. 0102 64位整数乘法
#include<iostream> using namespace std; typedef long long LL; LL a, b, p; LL res(LL a, LL b, LL q){ LL ret = 0, t = 1; while(b){ if(b & 1) ret = (ret + a) % q; b >>= 1; a = a * 2 % q; } return ret % q; } int main(){ cin>>a>>b>>p; cout<<res(a,b,p)<<endl; return 0; }
3. POJ1995 Raising Modulo Numbers
#include<iostream> using namespace std; int T, H, M; typedef long long LL; LL sum; void calc(int x, int y){ LL t = 1; while(y){ if(y & 1) t = (t * x) % M; x = 1ll * x * x % M; y >>= 1; } sum = (sum + t) % M; } int main(){ cin>>T; while(T--){ cin>>M>>H; sum = 0; for(int i = 0; i < H; ++i){ int l, r; cin>>l>>r; calc(l, r); } cout<<sum <<endl; } return 0; }
4. 0104 起床困难综合症
#include<iostream> #include<cstdio> #include<map> using namespace std; typedef pair<string, int> PSI; const int N = 1e5+10, M = 1e9; PSI op[N]; int n, m; int calc(int bit, int num){ for(int i = 1; i <= n; ++i) if(op[i].first == "AND") num &= op[i].second >> bit; else if(op[i].first == "OR") num |= op[i].second >> bit; else num ^= op[i].second >> bit; return num & 1; } int main(){ #ifdef _DEBUG freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); #endif cin>>n>>m; for(int i = 1; i <= n; ++i) cin>>op[i].first>>op[i].second; int res = 0, ans = 0; for(int i = 29; ~i; --i){ int l = calc(i, 0), r = calc(i, 1); #ifdef _DEBUG cout<<i<<' ' << l << ' '<<r<<endl; #endif if(res + (1 << i) <= m && l < r){ ans += r << i; res += 1 << i; } else ans += l << i; } cout<<ans<<endl; return 0; }
5. 0103 最短Hamilton路径
#include<iostream> #include<cstring> using namespace std; const int N = 1 << 20, M = 20; int f[N][M], path[M][M]; int n; int main(){ cin>>n; for(int i = 0; i < n ;++i){ for(int j = 0; j < n; ++j){ cin>>path[i][j]; } } memset(f, 0x3f, sizeof f); f[1][0] = 0; for(int i = 1; i < (1 << n); ++i){ for(int j = 0; j < n; ++j){ if(i & (1 << j)){ for(int k = 0; k < n; ++k){ if(((i - (1 << j)) & (1 << k))){ f[i][j] = min(f[i][j], f[i - (1<<j)][k]+path[k][j]); } } } } } cout<<f[(1<<n)-1][n-1]<<endl; return 0; }