0X01 位运算笔记
位运算,经常可以用来处理一些数学或动归方面的问题,通常会在数据范围较小的情况下使用。
为方便起见,一个 \(\mathrm{n}\) 位二进制数从右到左分别为第 \(\mathrm{0 \sim n - 1}\) 位。
快速幂 (\(\texttt{ACW89 a^b}\))
求 \(\mathrm{a^b}\) 对 \(\mathrm{p}\) 取模的值。
\(a, b \leq 10 ^ 9, 1 \leq p \leq 10 ^ 9\)。
考虑 \(b\) 的二进制。若可以表示为 \(\sum_{i}^{} 2^i\) 的形式,那么 \(a^b\) 就可以表示成 \(a ^ {\sum_{i}^{} 2 ^ i}\)。
而 \(a ^ {2 ^ i} = (a^{2^{i - 1}}) ^ 2\),这样可以通过递推的方式求出 \(a ^ {2 ^ i}\)。由于 \(\max\{i\}\) 是 \(\log b\) 级别的,所以总时间复杂度为 \(O(\log b)\)。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
using LL = long long;
int qpow(int a, int b, int p) {
int ans = 1 % p; // 这里是为了避免 p = 0 的情况出现
while (b) {
if (b & 1) ans = (LL)ans * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return ans;
}
int main() {
int a, b, p;
scanf("%d%d%d", &a, &b, &p);
printf("%d\n", qpow(a, b, p));
return 0;
}
六十四位整数乘法 (\(\texttt{ACW90}\))
求 \(a * b\) 对 \(p\) 取模的值。
- 算法一:
我们可以用类似快速幂的思想,使用二进制优化算法。将 \(b\) 拆分成二进制来看。假设 \(b\) 拆分成二进制之后是 \(\sum 2^i\),那么答案就是 \(a ^ {\sum 2 ^ i} = \prod a ^ {2 ^ i}\)。由于 \(\max_i \leq \log_2 b\) ,因此可以保证复杂度为 \(O(\log b)\)。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
using LL = long long;
LL mul(LL a, LL b, LL p) {
LL ans = 0;
while (b) {
if (b & 1) ans = (ans + a) % p;
a = (a << 1) % p;
b >>= 1;
}
return ans;
}
int main() {
LL a, b, p;
scanf("%lld%lld%lld", &a, &b, &p);
printf("%lld\n", mul(a, b, p));
}
- 算法二:
\(a \times b \pmod p = a \times b - \left \lfloor a \times b / p \right \rfloor\) 。
因此可以使用 \(\operatorname{long double}\) 存储 \(\left \lfloor a \times b / p \right \rfloor\),\(\operatorname{long long}\) 存储 \(a \times b\)。二者相减即可。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
using LL = long long;
LL mul(LL a, LL b, LL p) {
a %= p, b %= p;
LL c = (long double)a * b / p;
LL ans = a * b - c * p;
ans += (ans < 0) ? p : 0;
ans -= (ans >= p) ? p : 0;
return ans;
}
int main() {
LL a, b, p;
scanf("%lld%lld%lld", &a, &b, &p);
printf("%lld\n", mul(a, b, p));
}
最短 \(\text{Hamilton}\) 路径 (\(\texttt{ACW91}\))
给定一张 \(n\) 个点的带权无向图,点从 \(0∼n−1\) 标号,求起点 \(0\) 到终点 \(n−1\) 的最短 \(\text{Hamilton}\) 路径。
\(\text{Hamilton}\) 路径的定义是从 \(0\) 到 \(n−1\) 不重不漏地经过每个点恰好一次。 \(n \leq 20\)。
考虑状态压缩 \(dp\)。由于 \(n\) 的规模很小,可以直接用二进制枚举每个点的状态,其中 \(1\) 表示经过一次,\(0\) 表示没有经过。
设置状态 \(f_{i, j}\) ,表示当前在第 \(i\) 个点,当前状态是 \(j\) 的最短路径。首先要枚举所有可行状态,刷表时枚举当前点和要走到的点,如果当前点没有被走到过或者要去的点已经走过一遍了,那么直接剪掉。最终答案即为 \(f_{n - 1, 2^n - 1}\)。
由此来看,复杂度为 \(O(2 ^ n n ^ 2)\),但是剪枝强度很大,可以跑过。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 21;
int f[N][1 << N];
int n, w[N][N];
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
for (int j = 0; j < n; j ++ )
scanf("%d", &w[i][j]);
memset(f, 0x3f, sizeof f);
f[0][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 << k)) continue;
f[k][i | (1 << k)] = min(f[k][i | (1 << k)], f[j][i] + w[j][k]);
}
}
printf("%d\n", f[n - 1][(1 << n) - 1]);
return 0;
}
起床困难综合征 (\(\texttt{ACW998}\))
本题思路是按位贪心。可以参照 \(01trie\) 进行理解。
形象地看,假设我有一个 \(n\) 位的二进制数,每一位都是 \(0\),现在我可以把某一位变成一使得这个数最大,那么我肯定要变 \(n - 1\) 位,因为我只要把最高位设成 \(1\),就会比 \(0 \sim n - 2\) 位都设成 \(1\) 要大 \(1\)。
本题也可以用这种思路,将答案从高位到低位按最优方案填充即可。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 100010;
int n, m, w[N];
string str[N];
int calc(int bit, int now) {
for (int i = 1; i <= n; i ++ ) {
int x = w[i] >> bit & 1;
if (str[i] == "AND") now &= x;
else if (str[i] == "OR") now |= x;
else now ^= x;
}
return now;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
cin >> str[i] >> w[i];
int val = 0, ans = 0;
for (int i = 29; ~i; i -- ) {
int res0 = calc(i, 0);
int res1 = calc(i, 1);
if (val + (1 << i) <= m && res0 < res1)
val += 1 << i, ans += res1 << i;
else ans += res0 << i;
}
cout << ans << endl;
return 0;
}
0X01 位运算笔记的更多相关文章
- Matlab位运算笔记
本文为转载其他地方的文章; MATLAB函数 1.matlab函数bitset 设置数的某一位二进制位为1. <Simulink与信号处理> 使用方法 C = bitset(A,bit) ...
- java位运算笔记
位运算: ~(非)-->二进制数进行0和1的互换 样例: public class Test { public static void main(String[] args) { System. ...
- 「算法竞赛进阶指南」0x01 位运算 知识笔记
二进制是计算机的根本! 你了解她它吗? int lowbit(int x) { return x&(-x);//x&(~x+1),~x=-1-x; } int __builtin_ct ...
- 0x01 位运算
都比较基础吧. 知识点 1.快速幂和快速乘(这里有一个用long double舍弃精度的做法,但是感觉既不稳又没用) 2.懒人写边目录的时候的k^1 3.lowbit,得到的是低到高第一个1的位.求一 ...
- 算法进阶:0x01 位运算
一.快速幂的模板代码 a^b%p: #include<iostream> using namespace std; int main() { int a,b,p; cin>>a ...
- c#位运算小例子笔记
关于位运算,网上有挺多好的博客介绍过,我就不多解释了 这里只记录一个小例子,是在理解位运算时候写的,帮助自己加深一下印象,做个笔记mark一下 具体场景 摇骰子游戏 1每个骰子有6个点,1-3为小,4 ...
- C#学习笔记-----C#枚举中的位运算权限分配
一.基础知识 什么是位运算? 用二进制来计算,1&2:这就是位运算,其实它是将0001与0010做位预算 得到的结果是 0011,也就是3 2.位预算有多少种?(我们就将几种我们权限中会 ...
- php学习笔记位运算
位运算 源码:用二进制表示一个数,这个码就是源码. 比如2====00000000 00000000 0000000 00000010 正数的反码 源码 补码都一样 负数的源码是符号位取反.第一个位 ...
- C语言学习笔记之位运算求余
我们都知道,求一个数被另一个数整除的余数,可以用求余运算符”%“,但是,如果不允许使用求余运算符,又该怎么办呢?下面介绍一种方法,是通过位运算来求余,但是注意:该方法只对除数是2的N次方幂时才有效. ...
- ios开发学习笔记004-进制与位运算
进制 二进制 0 1组成,封2进1 八进制 0-7组成,封8进1 十进制 0-9组成,封10进1 十六进制 0-15组成,封16进1 printf以不同进制形式进行输出 变量的内存地址形式 变量在 ...
随机推荐
- OPPO主题组件开发 - 调试与预览
本篇作为 OPPO主题组件调试与预览 文档的补充,因为它真的很简单而且太老,一些命令已发生变化 1. 调试前准备 1. PC 端下载 adb命令工具 下载 https://adbdownload.co ...
- 原创基于Scrum框架产研团队运作20问
学习完了 Scrum,实际使用中,是否遇到/思考过下面的问题? Product Owner的老板是谁.谁来给 Product Owner打绩效.考核的标准是啥? Scrum Master 的老板是谁. ...
- 2023_10_10_MYSQL_DAY_02_笔记
2023_10_10_MYSQL_DAY_02_笔记 #在 FROM 子句中使用子查询 SELECT a.ename, a.sal, a.deptno, b.salavg FROM emp a, (S ...
- 【IOC,AOP】spring的基础概念
IOC 控制反转 对象的创建控制权转交给外部实体,就是控制反转.外部实体便是IOC容器.其实就是以前创建java对象都是我们new一下,现在我们可以把这个new交给IOC容器来做,new出来的对象也会 ...
- P8679 [蓝桥杯 2019 省 B] 填空问题 题解
P8679 [蓝桥杯 2019 省 B] 填空问题 题解 题目传送门 欢迎大家指出错误并联系这个蒟蒻 更新日志 2023-05-25 21:02 文章完成 2023-05-27 11:34 文章通过审 ...
- Windows下VC++编译器32位memcpy、memmove函数汇编代码详解
整理者:赤勇玄心行天道 QQ号:280604597 微信号:qq280604597 QQ群:511046632 博客:www.cnblogs.com/gaoyaguo blog.csdn.net/c ...
- 【Unity3D】UI Toolkit数据动态绑定
1 前言 本文将实现 cvs 表格数据与 UI Toolkit 元素的动态绑定. 如果读者对 UI Toolkit 不是太了解,可以参考以下内容. UI Toolkit简介 UI Toolki ...
- Hall定理(霍尔定理)证明及推广
引言 网络上有许多Hall定理的证明,但是对于Hall定理的几个推广的介绍却少之又少,因此本文来简单介绍一下 注:为了使这篇文章看起来简单易懂,本文将不会使用图论语言,会图论的朋友们可以自行翻译为图论 ...
- 新手面对安卓6.0以上的版本时出现一个关于文件权限检测的问题,报错为:“无法解析符号 'checkSelfPermission'”,解决办法
[[注意]:这只是笔者在遇到这个问题时的解决方法,如果对您毫无帮助,请自寻他法!!!] 面对新手:在简单做一个音乐播放程序时,如果面对安卓6.0以上的版本,就会出现一个关于文件权限检测的问题,报错为: ...
- 如何打开关闭Edge鼠标手势
概述 今天突然发现edge有了一个鼠标手势,用起来不是很适应,所以决定将其关掉. 操作步骤 点击edge右上角的三个点 在下拉菜单中选择设置 点开设置之后出现如图所示的界面,然后选择外观 往下面拉页面 ...