Atcoder 的“专属”头文件(AC library)介绍 [不完美的预告] [发现一些错误,准备自爆]

· · 算法·理论

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,满足对于任何 aa \: \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 中的 iseg.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$。