Atcoder Library 配置入门
配置
首先,你需要在这个 blog 里面下载 Atcoder Library 的压缩包。可以发现里面有三堆东西,一个 python 程序,两种语言的 document,还有一个库文件夹。
把库文件夹直接拖到你的编译器库文件相同目录下。Mingw 的路径应该都是 \lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++
,如果不是也容易自己寻找一下。或者定义环境变量 CPLUS_INCLUDE_PATH
,然后把 ac-library
文件夹的路径粘进去。
需要使用时直接引用库 #include <atcoder/all>
,并 using namespace atcoder;
。
约定:存在宏定义 ll
表示 long long
,ull
表示 unsigned long long
,uint
表示 unsigned int
。
内容
只介绍一部分比较有用的。
static_modint <mod>
类型
该类型的声明形如 static_modint <mod> x;
,其中 是一个 的整数。还有 modint998244353
和 modint1000000007
两个自带类型(前者表示 static_modint <998244353>
,后者同理)。
ACL 为其重载了四则运算(除法需要保证有逆元)和判断符号,包括 --
,++
,+=
等运算符。
需要注意任何与 static_modint <mod>
类型的运算都会将常数强转为此类型导致取模过多造成效率下降。你可以使用 static_modint <mod>::raw(int x)
直接返回 而不经取模,需要保证 ,否则行为未定义。
你可以使用 int x.val()
读取一个 static_modint <mod>
类型变量 的值用于其他 int
操作(例如输入输出)。
你可以使用 static_modint <mod> x.pow(ll n)
得到 。
你可以使用 static_modint <mod> x.inv()
得到 的逆元。
dynamic_modint
类型
该类型的声明形如 dynamic_modint <mod> x;
,其中 是一个 的整数。也可以简写为 modint x;
,它表示 dynamic_modint <-1> x;
,注意此时必须重新设置模数,否则行为未定义。
使用 void dynamic_modint <mod>::set_mod(int m)
将某种类型的 修改为 ,其中 是一个 的整数。你还可以使用 int dynamic_modint <mod>::mod()
来获取该类型此时的模数但我不知道有什么用。
其他使用方式同 static_modint
。注意该类型不能用于 ACL 内置的 NTT 等。
卷积
计算 。返回一个大小为 的 vector
表示 ,第 项表示 。
包含两个函数:
vector <T> convolution<mod>(vector <T> a, vector <T> b)
vector <ll> convolution_ll(vector <ll> a, vector <ll> b)
其中 T
是 int, uint, ll, ull
其中之一或者 ACL 的 static_modint
类型。
前者基于 NTT 实现,所以你需要保证 是一个满足存在 使得 且 的质数。如果不填入 <mod>
,则默认模数 。时间复杂度同 NTT,是 。
后者需要保证卷出来的结果都在 long long
范围内,且 。时间复杂度 。
dsu
本质是一个结构体。你可以使用 dsu d(int n)
声明并初始化 一个大小为 名为 d
的并查集。注意点的编号是从 开始的。
int d.merge(int a, int b)
:将 连接在一起,并返回连通块的代表元。bool d.same(int a, int b)
:返回 是否在同一连通块内。int d.leader(int a)
:返回 所在连通块的代表元。int d.size(int a)
:返回 所在连通块的大小。vector<vector<int>> d.groups()
:返回所有连通块包含的点的集合的集合。
Fenwick Tree(树状数组)
本质是一个结构体。你可以使用 fenwick_tree<T> fw(int n)
声明并初始化为全 一个大小为 类型为 T
名为 fw
的树状数组。其中 T
是 int, uint, ll, ull
其中之一或者 ACL 的 static_modint
类型。注意下标的编号是从 开始的。
void fw.add(int p, T x)
:单点 加上 。T fw.sum(int l, int r)
:查询 的区间和。注意如果T
是int, uint, ll, ull
其中之一,如果答案超出范围,则会自然溢出(对 取模)。
(带懒标记的)线段树
我愿称之为最吊。
考虑线段树的数学意义,其实就是半群单点修改和区间求积。所以我们要重载半群信息和乘法。你需要确保自己的半群乘法具有结合律。事实上,对于合理的半群信息维护,你总能将形式写成矩阵乘法,所以总是具有结合律。
你有两种方式 建立一棵包含懒标记的线段树,同样注意下标的编号是从 开始的:
lazy_segtree<S, op, e, F, mapping, composition, id> seg(int n);
lazy_segtree<S, op, e, F, mapping, composition, id> seg(vector<S> v);
前半部分是相同的:
S
是一个结构体,包含了所有你的标记和答案所需要维护的半群信息。op
是一个函数S op(S x, S y)
,表示将信息 左乘信息 合并在一起得到的信息(即 )。e
是一个函数S e()
,表示信息的左乘单位元,也就是满足 的 。F
是一个结构体,包含了你的标记信息。mapping
是一个函数S mapping(F x, S y)
,表示将标记 左乘信息 合并在一起得到的信息。composition
是一个函数F composition(F x, F y)
,表示将标记 左乘标记 ,也就是将标记 合并到 得到的标记(即 )。id
是一个函数F id()
,表示标记的左乘单位元,也就是合并到其他标记上不会影响其他标记的标记。也就是满足 的 。
后半部分,对于前者表示生成一棵维护 的线段树,目前每个元素为信息的单位元 ;对于后者表示生成一棵维护 的线段树,目前每个元素为 v
中的对应元素。
然后你可以使用以下函数:
void seg.set(int p, S x)
:单点修改 上的信息为 。S seg.get(int p)
:单点查询 上的信息。S seg.prod(int l, int r)
:区间查询左闭右开区间 的区间半群信息。对于 返回单位元。对于 行为未定义。S seg.all_prod()
:查询 的整个半群信息,时间复杂度 。void seg.apply(int p, F f)
:单点修改,将标记 合并到 上的信息。void seg.apply(int l, int r, F f)
:区间修改,将标记 合并到 的所有信息。
还有一些线段树上二分操作。
你需要定义一个确定性的函数 bool g(S x)
,需要满足其你所需的二分单调性。特别地,你需要使得 g(e())
的返回值为 true
。
int seg.max_right<g>(int l)
函数:返回右侧第一个 使得 的区间信息的 值是true
且 的区间信息的 值是false
。特别地,如果所有值均为true
则返回 ;如果所有值均为false
则返回 。int seg.min_left<g>(int r)
函数:返回左侧第一个 使得 的区间信息的 值是false
且 的区间信息的 值是true
。特别地,如果所有值均为true
则返回 ;如果所有值均为false
则返回 。
以上操作无特殊说明时间复杂度均为 。当然还要乘上标记或信息合并的复杂度 。
对于不带懒标记的线段树,主体部分只实现 S, op(), e()
就足够了。
数学库(类欧、逆元、快速幂、CRT)
ll pow_mod(ll x, ll n, int m)
:返回 。ll inv_mod(ll x, ll m)
:返回 在模 意义下的逆元。ll floor_sum(ll n, ll m, ll a, ll b)
:使用类欧,返回 ,复杂度 。你需要保证 。pair<ll, ll> crt(vector<ll> r, vector<ll> m)
:使用 ExCRT,返回方程组 的解。若无解,返回 ,否则返回 表示解为 ,其中 。若方程数量为 则返回 。你需要保证 在long long
范围内。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现