lyd读书笔记 0x01位运算

回来了一本算法竞赛进阶指南。可喜可贺,可喜可贺。
于是怀揣着期末考试GG的决心,我….开刷了(x)
顺便一提。。打*的题太丧病了所以就不做了。。


基本概念

bit是度量信息的基本单位。
0x表示16进制,0xff代表-1,0x7f代表128。
在m位二进制中,从右到左最低位为第0位,最高位为第m-1位。
我们常用0x3f3f3f3f表示最大值,因为二倍是int最大整数,每8位相同。
memset语句只能赋值出每8位相同的int,memset(a, 0x3f, sizeof a)可以用来初始化成最大值。
补码能比反码多表示一个数,减少了特殊判断,在电路设计中很简洁。
1<<n 1 << n 表示 2n 2 n n<<1 n << 1 表示 n×2 n × 2 n>>1 n >> 1 表示 n2.0 ⌊ n 2.0 ⌋
所以我学了啥

位运算优先级:
+ - > > << >> > > < == != > > & > ^ > > |

快速幂与慢速乘

快速幂模板题 POJ1995

#include <iostream>
using namespace std;
typedef long long LL;
LL T, n, m, qwq, tmpa, tmpk, ans;
inline LL ksmqm(LL a, LL k, LL mod) {
    LL ans = 1;
    for(; k; k >>= 1) {
        if(k & 1) ans = ans * a % mod;
        a = a * a % mod;
    }
    return ans;
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    cin>>T;
    while(T--) {
        ans = 0;
        cin>>m;
        cin>>n;
        while(n--) {
            cin>>tmpa>>tmpk;
            ans = (ans + ksmqm(tmpa, tmpk, m)) % m;
        }
        cout<<ans<<endl;
    }
    return 0;
}

慢速乘法
法一:对b二进制拆分然后跑一遍快速幂,O(logb)
法二:玄学的过程?

LL mul(LL a, LL b, LL p){
    a %= p, b %= p;
    LL c = (long double) a * b - c * p;
    LL ans = a * b - c * p;
    if(ans < 0) ans += p;
    else if(ans >= p) ans -= p;
}

二进制状态压缩

取出第k位 (n >> k) & 1
取出后k位 n & ((1 << k) - 1)
取反第k为 n ^ (1 << k)
第k位赋值1 n | (1 << k)
第k位赋值0 n & (~(1 << k))

例题
例二状压DP。。。
POJ2288打了*先不讨论

Hamilton路径
求从节点0到节点n - 1的最短Hamilton路径。其中Hamilton路径指从1~n恰好不重不漏的经过每个节点一次。 n20 n ≤ 20

状态设计:用dp[s][i]表示在节点i处状态为s,s的每一位分别指代某个节点是否访问过。
初始化:dp[s][i] = INF, dp[1][0] = 0表示从起点出发的路一开始长度是0。
转移:dp[s][i] = min{dp[s ^ (1 << j)][k] + w[k][j]},其中k、j已访问过(用i >> j & 1即可判断),表示从j节点没有被访问过的情况转移到这里。
结果:dp[(1 << n ) - 1][n - 1],表示终点处且每个点都访问过的情况。
复杂度 O(n22n) O ( n 2 ⋅ 2 n )

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3fffff
int w[26][26];
int dp[1 << 20][20];
int n, m, f, t, va;
int main() {
    scanf("%d%d", &n, &m);
    memset(w, 0x3f, sizeof w);
    for(int i = 1; i <= m; ++i) {
        scanf("%d%d%d", &f, &t, &va);
        --f; --t;
        w[f][t] = va;
        w[t][f] = va;
    }
    memset(dp, 0x3f, sizeof dp);
    dp[1][0] = 0;
    int M = 1 << n;
    for(int i = 1; i < M; ++i) {
        for(int j = 0; j < n; ++j)
        if(i >> j & 1)
            for(int k = 0; k < n; ++k)
            if(i >> k & 1)
                dp[i][j] = min(dp[i][j], dp[i ^ 1 << j][k] + w[k][j]);
    }
    cout<<dp[M - 1][n - 1];
    return 0;
} 
/*
4 5
1 2 6
2 3 7
0 3 5
1 3 1
2 0 2
*/

其它运算

成对变换:
n为偶数,n ^ 1为n + 1
n位奇数,n ^ 1为n - 1

lowbit:

inline int lowbit(int x) {return x & -x;}

用来取出最低位的1及其后面的0构成的数值。
原理好麻烦的样子(x

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值