Atcoder 的“专属”头文件(AC library)介绍 [不完美的预告] [发现一些错误,准备自爆]
_ACGODs_
·
·
算法·理论
Atcoder 的“专属”头文件介绍
Flag:新年前做完。
先投一点看看。
概述
大家在打 Atcoder 比赛时可能会遇到如下困难:板子打错死死调不出来、想到了一个好的解法却因为没学过 / 太麻烦了不愿意打而没实现出来。
Atcoder 有一套专属的头文件可以“缓解”这种情况。
这套头文件叫“AC Library”,可以帮助你在 Atcoder 上更好的打比赛。你可以在这里练习和体验 AC Library。
贴错链接是已知问题,不打算修改,比赛就在里面后三个链接里。(20251214)
注意:切勿对该系列头文件产生依赖。
基础使用方法
如果你需要使用该系列的头文件,请在程序开头加入 #include<atcoder/all>(包含所有 AC Library 中的头文件) 或之后的章节中提到的 atcoder/ 目录下的其他头文件。
请导入命名空间 atcoder 或在所有 AC Library 接口的开头加入 atcoder::。
你可能需要的新框架:
#include <atcoder/all>
#include <bits/stdc++.h>
using namespace std;
using namespace atcoder;
signed main(){
return 0;
}
注意
以下所有数据结构中的“第 i 个位置”字眼均为原数组上第 i 个位置的值,也就是“a_i”。
下标从 0 开始。
树状数组 / fenwicktree
可以维护单点加和区间和,时间复杂度均为 O(\log n)。
创建
创建:fenwick_tree<T> fw(int n),表示创建一个树状数组,元素类型为 T,容量为 n。
修改
单点加:fw.add(int i,T x),表示在第 i 个位置加上 x。
查询
区间和:fw.sum(int l,int r),表示 [l,r) 区间的和。
线段树 / segtree
本线段树不含懒标记,所以仅支持单点修改和区间查询还有一些二分,若需要区间修改,请移步含懒标记的线段树一章。
该数据结构可以维护一种符合幺半群的运算。
符合幺半群的运算:有一种运算 \text{op},满足 (a \: \text{op} \: b)\:\text{op}\ c=a\:\text{op}\: (b\:\text{op}\: c) 且有一常量 e,满足对于任何 a,a \: \text{op} \: e=a。
创建
创建:segtree<T,op,e> seg(int n),表示创建一个线段树,其类型为 T,其运算为 \text{op},其“常量”为 e,容量为 n。
修改
单点修改:seg.set(int i,T x),表示将第 i 个位置上的值修改为 x。
提示:单点 \text{op} 可以用该方法实现:seg.set(int i,op(seg.get(i),T x)),将第 i 个位置上的值修改为 i \: \text{op} \: x,注意 seg.get 中的 i 和 seg.set() 中的 i 必须相等。
查询
单点查询:seg.get(int i),查询第 i 个位置上的值。时间复杂度 O(1)。
区间查询:seg.prod(int l,int r),查询第 l 到第 r 位置上的操作和。也就是 a_l \: \text{op} \: a_{l+1} \: \text{op} \dots \text{op} \: a_{r-1}。
整体查询:seg.all_prod(),等同于 seg.prod(0, n)。
二分
指定左端点:``seg.max_right<f>(int l)``,返回一个最小的 $r$,它满足 $f(\text{op}(a_l,a_{l+1},\dots,a_{r-1}))=1$($f$ 函数为 bool 类型)。
指定右端点:``seg.min_left<f>(int r)``,返回一个最小的 $l$,它满足 $f(\text{op}(a_l,a_{l+1},\dots,a_{r-1}))=1$($f$ 函数为 bool 类型)。
## 自动取模的整数 / modint
这个东西可以帮你在运算时自动取模。仅此而已。
### 创建
``modint998244353 a = x``,定义一个“整数”,它的初始值为 $x$,此后每次运算时都会自动对 $998244353$ 取模。
``modint1000000007 a = x``,和上自动对 $998244353$ 取模的整数差不多,只不过模数换成了 $10^9+7$。
``modint a = x``,未指定模数的 ``modint``,读写运算要在 ``set_mod`` 后做。
``modint::set_mod(x)``,(基础用法)对所有未指定模数的 ``modint`` 统一设置模数 $x$,比较高阶的用法后面讲。
``static_modint<mod>``,指定模数 $\bmod$,运算时自动取模,不可 ``set_mod``。
### 读写运算
``x.val()``,返回 ``modint`` 对象 $x$ 内的值。
```cpp
-x
++x
--x
x++
x--
x+y
x-y
x*y
x/y
x+=y
x-=y
x*=y
x/=y
x==y
x!=y
```
均可像正常整数那样做运算,只不过运算后会自动取模。
另外,``modint`` 的“等级”比一般整数类型高一些,所以一个普通整型和一个 ``modint`` 做运算,会返回一个 ``modint``。
``x.pow(n)``,将 $x$ 设为 $x^n$。
``x.inv()``,返回 $x$ 对于它的模数的逆元。逆元的定义:设 $x$ 的模数为 $m$,那么 $x$ 的逆元就是同余方程 $xy \equiv 1\,(\text{mod}\; m)$ 的解中的 $y$。
### 注意事项
求逆元时,必须保证 $\gcd(x,m)=1$。
做除法时,必须保证 $\gcd(y,xm)=1$。$xm$ 为 $x$ 的模数。
### set_mod 的高阶用法
``dynamic_modint<type>``,表示创建一个 ``dynamic_modint`` 家族。
如果我们定义一个 ``using m0 = dynamic_modint<0>``,那么我们如果执行 ``m0::setmod(x)``,就只会将 $\text{m0}$ 家族内所有 ``modint`` 对象的模数设为 $x$。
另外,``modint`` 是 ``dynamic_modint<-1>`` 的别名。
## 数学 / math
``pow_mod(x,n,m)``,返回 $x^n \bmod m$。
``inv_mod(x,m)``,返回 $x$ 对于 $m$ 的逆元。
`` pair<ll, ll> crt(vector<ll> r, vector<ll> m)``,返回以下同余方程的共同解(``first`` 项):
$$x\equiv r_i \;(\bmod \; m_i)$$
和(``second`` 项)$\text{lcm}(m_1,m_2,\dots,m_n)$。
``floor_sum(n,m,a,b)``,我也不知道是干嘛的,反正会返回这么个东西:$\sum_{i=0}^{n-1}\lfloor{\frac{ai+b}{m}}\rfloor$。