OI & ACM 笔记:A - 基础算法
A - 基础算法 指令基础
NOI Linux 指令
返回上一级目录:cd ..
。
返回当前用户的主目录:cd ~
。
进入某一目录内:cd <目录路径>
。
显示当前工作目录:pwd
。
新建文件夹:mkdir <目录文件> <新建文件夹名>
。
删除:
- 删除单一文件:
rm <文件路径>
。 - 删除目录及目录内的文件:
rm -r <要删除的目录路径>
。
改名:mv <旧文件名> <新文件名>
查看文件内容:cat <文件路径>
。
简单查看文件信息:dir <目录路径>
。
详细查看文件信息:ls -alh <目录路径>
。
ls -a <目录路径>
:显示所有文件(包括隐藏文件)。ls -l <目录路径>
:显示文件属性。ls -h <目录路径>
:将文件大小转换为常用单位(无单位默认为字节)。
提升权限:
- 使用 root 权限(最高权限)操作该命令:
sudo <命令>
。 - 登录到另外一个用户:
su <用户名>
。
命令行编译:g++/gcc <源代码文件名> -o <可执行文件名>
。
-std=c++11
:c++11 标准。-O2
:O2 优化。ulimit -s 128000000
:开栈。-Wall
:显示所有警告。-Wextra
:检测可疑代码并生成警告。-Wconversion
:转型警告。
执行程序:./<应用程序>
。
只读执行程序:./<project> <A.in
。
只输执行程序:./<project> >A.out
。
读输执行程序:./<project> <A.in >A.out
。
查看执行程序运行时间:time ./<应用程序>
。
比较文本:diff <文本 A><文本 B>
。
A - 基础算法 语言基础
语言基础
数值范围:
int
范围:\(< 2^{31}\)。unsigned int
范围:\(< 2^{32}\)。long long
范围:\(< 2^{63}\)。unsigned long long
范围:\(< 2^{64}\)。
自然溢出:
unsigned int
自然溢出:对 \(2^{32}\) 取模。unsigned long long
自然溢出:对 \(2^{64}\) 取模。
输出格式:
unsigned int
输出格式:%u
。unsigned long long
输出格式:%llu
。
C++ STL
lower_bound & upper_bound
lower_bound(s, t, val)
:指向 \([s, t)\) 中第一个 \(\geq \mathrm{val}\) 的元素的迭代器,需要保证 \([s, t)\) 升序排序。upper_bound(s, t, val)
:指向 \([s, t)\) 中第一个 \(> \mathrm{val}\) 的元素的迭代器,需要保证 \([s, t)\) 升序排序。
sort
sort()
的实现基于快速排序。
升序排序(默认):sort(s, t)
。
降序排序:sort(s, t, greater<int>())
。
stable_sort
stable_sort()
的实现基于归并排序。比较次数稳定且相较于std::sort
较少。
vector
- vector 编号从 \(0\) 开始。
vector<int> num(n)
:指定该容器大小为 \(n\)。vector<int> num(n, m)
:指定该容器大小为 \(n\),初始值均为 \(m\)。V.resize(n, m)
:重新制定该容器大小为 \(n\),若容器变长,则以值 \(m\) 填充;若容器变短,则超出限制的元素被删除。lower_bound(V.begin(), V.end(), val)
:指向容器中第一个 \(\geq \mathrm{val}\) 的元素的迭代器,需要保证容器升序排序。upper_bound(V.begin(), V.end(), val)
:指向容器中第一个 \(> \mathrm{val}\) 的元素的迭代器,需要保证容器升序排序。
区间计数: upper_bound(V.begin(), V.end(), r) - lower_bound(V.begin(), V.end(), l)
。
priority_queue
大根堆(默认):priority_queue<int> q
。
小根堆:priority_queue< int, vector<int>, greater<int> > q
。
set
升序排序(默认):set<int> s
。
降序排序:set< int, greater<int> > s
。
s.lower_bound(val)
:指向容器中第一个 \(\geq \mathrm{val}\) 的元素的迭代器。s.upper_bound(val)
:指向容器中第一个 \(> \mathrm{val}\) 的元素的迭代器。
multiset
升序排序(默认):multiset<int> s
。
降序排序:multiset< int, greater<int> > s
。
s.erase(val)
:将所有数值为 \(\mathrm{val}\) 的元素删除,返回被删除的元素个数。s.erase(pos)
:将迭代器 \(\mathrm{pos}\) 指向的元素删除。- 在 multiset 中删除一个数值为 \(\mathrm{val}\) 的元素,应执行
s.erase(s.find(val))
。 - 使用 multiset 维护多关键字的数据,在重载小于号时,应保证两个不同的元素能被有效区分。
bitset
- bitset 编号从 \(0\) 开始。
- bitset 可以进行各种二进制操作,如取反
~
、与&
、或|
、异或^
、左移<<
、右移>>
。 b.count()
:返回 \(1\) 的个数。b.any()
:查询 bitset 中是否存在 \(1\)。b.none()
:查询 bitset 中是否全 \(0\)。b.reset()
:将所有位改为 \(0\)。b.set()
:将所有位改为 \(1\)。b._Find_first()
:查询低位到高位第一个 \(1\) 的位置。b._Find_next()
:查询当前位置之后下一个 \(1\) 的位置。
A - 基础算法 算法基础
I/O 优化
读入优化:
template <class T>
inline void read(T &x) {
static char s;
static bool opt;
while (s = getchar(), (s < '0' || s > '9') && s != '-');
x = (opt = s == '-') ? 0 : s - '0';
while (s = getchar(), s >= '0' && s <= '9') x = x * 10 + s - '0';
if (opt) x = ~x + 1;
}
快速乘
\(\mathcal{O}(\log n)\) 快速乘:
特别要注意的是,模数大小的两倍不能超过 long long
上界。
typedef long long s64;
s64 qmul(s64 a, s64 b, s64 p) {
s64 ans = 0;
for (; b; b >>= 1) {
if (b & 1) ans = (ans + a) % p;
a = 2 * a % p;
}
return ans;
}
\(\mathcal{O}(1)\) 快速乘:
注意到:
利用 long double
来处理 \(\left\lfloor \frac{a \times b}{p} \right\rfloor\)。
虽然 \(a \times b\) 和 \(\left\lfloor \frac{a \times b}{p} \right\rfloor \times p\) 的数值可能很大,但是两者的差一定在 \([0, p)\) 之间,我们只关心它们的前 64 位即可,这正好可以用 long long
运算的自然溢出来处理。
typedef long long s64;
s64 qmul(s64 a, s64 b, s64 p) {
s64 c = (long double)a * b / p + 1e-8;
s64 ans = a * b - c * p;
if (ans < 0) ans += p;
if (ans >= p) ans -= p;
return ans;
}
快速幂
int qpow(int a, int b, int p) {
int ans = 1;
for (; b; b >>= 1) {
if (b & 1) ans = 1ll * ans * a % p;
a = 1ll * a * a % p;
}
return ans;
}
二分
>> 1
向下取整,/2
向零取整。- 在整数域上进行二分时:
- 若分成的区间为 \([l, \mathrm{mid}], [\mathrm{mid} + 1, r]\),
mid = (l + r) >> 1
。 - 若分成的区间为 \([l, \mathrm{mid} - 1], [\mathrm{mid}, r]\),
mid = (l + r + 1) >> 1
。
- 若分成的区间为 \([l, \mathrm{mid}], [\mathrm{mid} + 1, r]\),
- 在实数域上进行二分时,
mid = (l + r) / 2
。
二叉树
\(n_0\) 与 \(n_2\) 的关系:\(n_0 = n_2 + 1\)。
\(n_0\) 与 \(n_2\) 的关系证明:
贪心
贪心的常见证明方法:
- 微扰法(邻项交换)。
- 范围缩放法。
- 决策包容性。
- 反证法。
- 数学归纳法。