├── .gitignore ├── 01 - 常用在线模板汇总 ├── 01 - 初始与杂类 │ ├── 00 - wida.h │ ├── 01 - 在线头文件.cpp │ ├── 01 - 在线头文件(简洁版).cpp │ ├── 02 - 基础函数重载.cpp │ ├── 03 - 对拍相关.md │ ├── 04 - 手工随机哈希.cpp │ ├── 05 - 全排列生成.cpp │ ├── 06 - 快读(fread).cpp │ ├── 07 - int128输入输出流控制.cpp │ └── 08 - 样例构造(随机数、树、图生成).cpp ├── 02 - 图、树、网络流 │ ├── 01 - 树的直径.cpp │ ├── 02 - 点分治.cpp │ ├── 03 - HLD.cpp │ ├── 04 - LCA(无权图树上倍增解).cpp │ ├── 04 - LCA(有权图树上倍增解).cpp │ ├── 04 - LCA(树链剖分解).cpp │ ├── 05 - Djikstra.cpp │ ├── 06 - Floyd.cpp │ ├── 07 - MST (Kruskal).cpp │ ├── 08 - SCC (Tarjan).cpp │ ├── 09 - EDCC (Tarjan)(新版).cpp │ ├── 09 - EDCC (Tarjan)(链式前向星旧版,yxc版本).cpp │ ├── 10 - VDCC (Tarjan).cpp │ ├── 11 - 一般图最大匹配(带花树算法)(新版).cpp │ ├── 11 - 一般图最大匹配(带花树算法)(旧版).cpp │ ├── 12 - 一般图最大权匹配(带权带花树算法).cpp │ ├── 13 - 二分图最大匹配 (HopcroftKarp).cpp │ ├── 14 - 2-Sat.cpp │ ├── 15 - 二分图最大权匹配 (KM).cpp │ ├── 16 - 最大流 (Dinic).cpp │ ├── 17 - 最大流 (HLPP).cpp │ └── 18 - 费用流 (Djikstra).cpp ├── 03 - 数论、几何 │ ├── 18 - 欧拉筛.cpp │ ├── 19 - Exgcd.cpp │ ├── 20 - 组合数 (Lucas).cpp │ ├── 21 - Miller-Rabin + Pollard-Rho.cpp │ ├── 22 - 平面几何.cpp │ └── 23 - 立体几何.cpp └── 04 - 串与数据结构 │ ├── 24 - DSU++.cpp │ ├── 24 - DSU.cpp │ ├── 25 - Fenwick 1D++.cpp │ ├── 25 - Fenwick 1D.cpp │ ├── 25 - Fenwick 2D++.cpp │ ├── 25 - Fenwick 2D.cpp │ ├── 26 - Segt++.cpp │ ├── 26 - Segt.cpp │ ├── 27 - 坐标压缩.cpp │ ├── 29 - 分数四则运算.cpp │ ├── 30 - ST 表.cpp │ ├── 31 - 线性基(高斯消元法).cpp │ ├── 32 - 取模类.cpp │ ├── 33 - 高精度大整数类.cpp │ ├── 34 - KMP.cpp │ ├── 35 - 字符串双哈希++.cpp │ ├── 35 - 字符串双哈希.cpp │ └── 35 - 字符串哈希.cpp ├── 02 - 打印稿模板汇总 ├── 01 - 基础算法.md ├── 02 - 树上问题.md ├── 03A - 图论.md ├── 03B - 图论常见结论及例题.md ├── 04 - 网络流总模板.md ├── 05A - 数论.md ├── 05B - 数论常见结论及例题.md ├── 06A - 二维几何.md ├── 06B - 多边形相关.md ├── 06C - 三维几何及常见例题.md ├── 07 - 多项式总模板.md ├── 08A - 数据结构.md ├── 08B - 数据结构.md ├── 09 - 动态规划.md ├── 10 - 串.md ├── 11 - 博弈论.md ├── 12 - 常用例题.md ├── 13 - STL与库函数.md ├── 14 - 杂类.md └── README.md ├── 03 - jiangly模板收集 ├── 01 - 杂类 │ ├── 01 - int128 库函数自定义.cpp │ ├── 02 - 常用库函数重载.cpp │ ├── 03 - 字符调整.cpp │ ├── 04A - 二分算法(整数域).cpp │ ├── 04B - 二分算法(实数域).cpp │ └── 05 - 快读.cpp ├── 02 - 图与网络 │ ├── 01 - 强连通分量缩点(SCC).cpp │ ├── 02 - 割边与割边缩点(EBCC).cpp │ ├── 03 - 二分图最大权匹配(MaxAssignment 基于KM).cpp │ ├── 04 - 一般图最大匹配(Graph 带花树算法)【久远】.cpp │ ├── 05 - TwoSat(2-Sat).cpp │ ├── 06A - 最大流(Flow 旧版其一,整数应用).cpp │ ├── 06B - 最大流(Flow 旧版其二,浮点数应用).cpp │ ├── 06C - 最大流(MaxFlow 新版).cpp │ ├── 07A - 费用流(MCFGraph 旧版).cpp │ ├── 07B - 费用流(MinCostFlow 新版).cpp │ ├── 08 - 树链剖分(HLD).cpp │ └── 09 - 圆方树.cpp ├── 03 - 数论、几何与多项式 │ ├── 01 - 快速幂.cpp │ ├── 02 - 基姆拉尔森公式.cpp │ ├── 03 - 欧拉筛.cpp │ ├── 04 - 莫比乌斯函数筛(莫比乌斯反演).cpp │ ├── 05 - 扩展欧几里得(exgcd).cpp │ ├── 06A - 欧拉函数(求解单个数的欧拉函数).cpp │ ├── 06B - 欧拉函数(求解全部数的欧拉函数).cpp │ ├── 07A - 取模运算类(Z).cpp │ ├── 07B - 取模运算类(MLong & MInt).cpp │ ├── 07C - 取模运算类(ModIntBase).cpp │ ├── 07D - 快速取模运算类(MontgomeryModInt32 蒙哥马利模乘).cpp │ ├── 07E - 大数快速取模运算类(Barrett & DynModInt, with ModIntBase 巴雷特约减模乘).cpp │ ├── 08A - 组合数(小范围预处理,逆元+杨辉三角).cpp │ ├── 08B - 组合数(Comb, with. ModIntBase).cpp │ ├── 09A - 平面几何(Point).cpp │ ├── 09B - 平面几何(with. complex).cpp │ ├── 10 - 立体几何(Point).cpp │ ├── 11A - 静态凸包(with. Point,旧版).cpp │ ├── 11B - 静态凸包(with. Point,新版).cpp │ ├── 11C - 静态凸包(with. complex).cpp │ ├── 12A - 多项式(Poly 手写取模).cpp │ ├── 12B - 多项式(Poly, with Z).cpp │ ├── 12C - 多项式(Poly, with MontgomeryModInt32).cpp │ ├── 12D - 多项式乘法.cpp │ ├── 12D - 多项式(Poly, with MInt).cpp │ ├── 13A - 生成函数(q-int).cpp │ ├── 13B - 生成函数(q-Binomial).cpp │ ├── 13C - 生成函数(Binomial 任意模数二项式).cpp │ ├── 14 - 自适应辛普森法(Simpson).cpp │ ├── 15 - 矩阵(Matrix).cpp │ ├── 16 - 高斯消元法 gaussian elimination(gauss)【久远】.cpp │ └── 17 - 素数测试与因式分解(Miller-Rabin & Pollard-Rho).cpp ├── 04 - 数据结构 │ ├── 01A - 树状数组(Fenwick 旧版).cpp │ ├── 01B - 树状数组(Fenwick 新版).cpp │ ├── 02A - 并查集(DSU).cpp │ ├── 02B - 可撤销并查集(DSU With Rollback).cpp │ ├── 03A - 线段树(SegmentTree+Info 区间加+单点修改).cpp │ ├── 03B - 线段树(SegmentTree 区间乘+单点加).cpp │ ├── 03C - 线段树(SegmentTree+Info 初始赋值+单点修改+查找前驱后继).cpp │ ├── 03D - 线段树(SegmentTree+Info+Merge 初始赋值+单点修改+区间合并).cpp │ ├── 04 - 懒标记线段树(LazySegmentTree).cpp │ ├── 05 - 高精度(BigInt).cpp │ ├── 06 - 状压RMQ(RMQ).cpp │ ├── 07A - Splay.cpp │ ├── 07B - Splay.cpp │ ├── 07C - Splay.cpp │ ├── 08A - 其他平衡树.cpp │ ├── 08B - 其他平衡树.cpp │ ├── 08C - 其他平衡树.cpp │ ├── 08D - 其他平衡树.cpp │ ├── 09 - 分数四则运算(Frac).cpp │ ├── 10 - 线性基(Basis).cpp │ └── 11 - Link-Cut Tree.md ├── 05 - 字符串 │ ├── 01A - 马拉车(Manacher).cpp │ ├── 02 - Z函数.cpp │ ├── 03A - 后缀数组(SuffixArray).cpp │ ├── 03B - 后缀数组(SA).cpp │ ├── 04A - 后缀自动机(SuffixAutomaton 旧版).cpp │ ├── 04B - 后缀自动机(SAM 新版).cpp │ ├── 05 - 回文自动机(PAM).cpp │ ├── 06A - AC自动机(AC 旧版).cpp │ ├── 06B - AC自动机(AhoCorasick, with vector 新版).cpp │ ├── 06C - AC自动机(AhoCorasick, with string 新版).cpp │ ├── 07 - 字符串哈希(随机底模例题).cpp │ ├── 08 - 最长公共前缀 LCP(例题).cpp │ ├── 09A - 字典树 Trie.md │ ├── 09B - 字典树 Trie.cpp │ └── 10 - 前缀函数(KMP).cpp └── README.md ├── 04 - 历年XCPC赛事补题链接整理 ├── 01 - 省赛与邀请赛.md ├── 02 - ICPC.md ├── 03 - CCPC.md └── README.md ├── 05 - 算法竞赛命题白皮书 └── README.md ├── 06 - 个人博客搬运 ├── 算法笔记 │ ├── 01 - pb_ds库探究.md │ ├── 02 - 浮点数探究.md │ ├── 03 - 哈希表的复杂度探究.md │ ├── 05 - LLVM(代码自动格式化)探究.md │ ├── 06 - p01.png │ ├── 06 - p02.png │ ├── 06 - p03.png │ └── 06 - 本地编译器编译设置.md └── 算法笔记与题单 │ ├── 01 - 博弈论.md │ ├── 02 - 前缀和.md │ ├── 03 - 二分与三分算法.md │ ├── 04 - 莫队算法.md │ ├── 06 - 位运算.md │ ├── 07 - 树上分治.md │ ├── 08 - Tarjan缩点.md │ ├── 09 - 网络流.md │ └── 10 - 字符串哈希.md ├── 07 - 论文与资料 ├── 【于纪平】C++的pb_ds库在OI中的应用.pdf └── 图的偏心距示意图.png ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/01 - 初始与杂类/01 - 在线头文件(简洁版).cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | #ifndef ONLINE_JUDGE 4 | #include 5 | #endif 6 | 7 | 8 | signed main() { 9 | int Task = 1; 10 | for (cin >> Task; Task; Task--) { 11 | 12 | } 13 | } 14 | 15 | int __OI_INIT__ = []() { 16 | ios::sync_with_stdio(0), cin.tie(0); 17 | cout.tie(0); 18 | cout << fixed << setprecision(12); 19 | return 0; 20 | }(); 21 | -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/01 - 初始与杂类/02 - 基础函数重载.cpp: -------------------------------------------------------------------------------- 1 | int mypow(int n, int k, int p = MOD) { // 复杂度是 log N 2 | int r = 1; 3 | for (; k; k >>= 1, n = n * n % p) { 4 | if (k & 1) r = r * n % p; 5 | } 6 | return r; 7 | } 8 | int mylcm(int x, int y) { 9 | return x / __gcd(x, y) * y; 10 | } 11 | int Log2(int x) { // 计算log2(x),向下取整 12 | return 31 - __builtin_clz(x); 13 | } 14 | int ceiledLog2(int x) { // 计算log2(x),但是向上取整 15 | return Log2(x) + (__builtin_popcount(x) != 1); 16 | } 17 | template T sign(const T &a) { 18 | return a == 0 ? 0 : (a < 0 ? -1 : 1); 19 | } 20 | template T floor(const T &a, const T &b) { // 注意大数据计算时会丢失精度 21 | T A = abs(a), B = abs(b); 22 | assert(B != 0); 23 | return sign(a) * sign(b) > 0 ? A / B : -(A + B - 1) / B; 24 | } 25 | template T ceil(const T &a, const T &b) { // 注意大数据计算时会丢失精度 26 | T A = abs(a), B = abs(b); 27 | assert(b != 0); 28 | return sign(a) * sign(b) > 0 ? (A + B - 1) / B : -A / B; 29 | } -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/01 - 初始与杂类/03 - 对拍相关.md: -------------------------------------------------------------------------------- 1 | ### C++ 对拍 2 | 3 | ```c++ 4 | #include 5 | using namespace std; 6 | int main() { 7 | for (int Case = 1; Case <= 10000; Case ++) { 8 | cout << "==============\n"; 9 | system("Juruo.exe"); 10 | double start = clock(); 11 | system("Shenben.exe"); 12 | double end = clock(); 13 | system("Ask.exe"); 14 | 15 | if (system("fc Juruo.out Shenben.out")) { 16 | cout << "\033[31m" << "Wrong Answer\n" << "\033[0m"; 17 | exit(0); 18 | } else { 19 | cout<< "\033[32m" << "Accept!\n"<< "\033[0m"; 20 | cout << "测试点 #" << Case << ",用时" << end - start << "ms\n"; 21 | } 22 | } 23 | return 0; 24 | } 25 | ``` 26 | 27 | ### bat 对拍 28 | 29 | ```bat 30 | :loop 31 | Ask 32 | Juruo 33 | Shenben 34 | fc Juruo.out Shenben.out 35 | if errorlevel==1 pause 36 | goto loop 37 | ``` 38 | 39 | ### 样例编造 40 | 41 | ```c++ 42 | #include 43 | using namespace std; 44 | #ifndef ONLINE_JUDGE 45 | #include 46 | #endif 47 | #define endl "\n" 48 | #define int long long 49 | 50 | mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count()); 51 | int r(int a, int b) { 52 | return rnd() % (b - a + 1) + a; 53 | } 54 | template void __(Args... args) { 55 | auto _ = [&](auto x) { cout << x << " "; }; 56 | int __[] = {(_(args), 0)...}; 57 | cout << "\n"; 58 | } 59 | 60 | signed main() { 61 | 62 | } 63 | 64 | int __Ask__ = []() { 65 | freopen("Ask.txt", "w", stdout); 66 | ios::sync_with_stdio(0), cin.tie(0); 67 | cout.tie(0); 68 | cout << fixed << setprecision(12); 69 | return 0; 70 | }(); 71 | 72 | ``` 73 | 74 | ### 文件读写函数 75 | 76 | 直接复制到文件底部即可,这样定义的空函数中的内容会自动先于 `main` 函数运行。 77 | 78 | ```c++ 79 | int __Shenben__ = []() { 80 | freopen("Ask.txt","r",stdin); 81 | freopen("Shenben.out","w",stdout); 82 | return 0; 83 | }(); 84 | ``` 85 | 86 | ```c++ 87 | int __Juruo__ = []() { 88 | freopen("Ask.txt","r",stdin); 89 | freopen("Juruo.out","w",stdout); 90 | return 0; 91 | }(); 92 | ``` 93 | -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/01 - 初始与杂类/04 - 手工随机哈希.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct myhash { 4 | static uint64_t hash(uint64_t x) { 5 | x += 0x9e3779b97f4a7c15; 6 | x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9; 7 | x = (x ^ (x >> 27)) * 0x94d049bb133111eb; 8 | return x ^ (x >> 31); 9 | } 10 | size_t operator()(uint64_t x) const { 11 | static const uint64_t SEED = chrono::steady_clock::now().time_since_epoch().count(); 12 | return hash(x + SEED); 13 | } 14 | size_t operator()(pair x) const { 15 | static const uint64_t SEED = chrono::steady_clock::now().time_since_epoch().count(); 16 | return hash(x.first + SEED) ^ (hash(x.second + SEED) >> 1); 17 | } 18 | }; 19 | #define mymap __gnu_pbds::gp_hash_table 20 | 21 | // mymap dic; 22 | // unordered_map dic; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/01 - 初始与杂类/05 - 全排列生成.cpp: -------------------------------------------------------------------------------- 1 | int n; 2 | cin >> n; 3 | vector a(n); 4 | // iota(a.begin(), a.end(), 1); 5 | for (auto &it : a) { 6 | cin >> it; 7 | } 8 | sort(a.begin(), a.end()); 9 | 10 | do { 11 | for (auto it : a) { 12 | cout << it << " "; 13 | } 14 | cout << endl; 15 | } while (next_permutation(a.begin(), a.end())); -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/01 - 初始与杂类/06 - 快读(fread).cpp: -------------------------------------------------------------------------------- 1 | // 注意,在当前较新的 C++ 版本中,使用 getchar() 的快读是无意义的,只有使用 fread() 的才能提速 2 | 3 | namespace QuickRead { // 快读 4 | char buf[1 << 21], *p1 = buf, *p2 = buf; 5 | inline int getc() { 6 | return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++; 7 | } 8 | template void Cin(T &a) { 9 | T ans = 0; 10 | bool f = 0; 11 | char c = getc(); 12 | for (; c < '0' || c > '9'; c = getc()) { 13 | if (c == '-') f = 1; 14 | } 15 | for (; c >= '0' && c <= '9'; c = getc()) { 16 | ans = ans * 10 + c - '0'; 17 | } 18 | a = f ? -ans : ans; 19 | } 20 | template void Cin(T &a, Args &...args) { 21 | Cin(a), Cin(args...); 22 | } 23 | template void write(T x) { // 注意,这里输出不带换行 24 | if (x < 0) putchar('-'), x = -x; 25 | if (x > 9) write(x / 10); 26 | putchar(x % 10 + '0'); 27 | } 28 | } // namespace QuickRead 29 | 30 | using namespace QuickRead; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/01 - 初始与杂类/07 - int128输入输出流控制.cpp: -------------------------------------------------------------------------------- 1 | namespace my128 { // 读入优化封装,支持__int128 2 | using i64 = __int128_t; 3 | i64 abs(const i64 &x) { 4 | return x > 0 ? x : -x; 5 | } 6 | auto &operator>>(istream &it, i64 &j) { 7 | string val; 8 | it >> val; 9 | reverse(val.begin(), val.end()); 10 | i64 ans = 0; 11 | bool f = 0; 12 | char c = val.back(); 13 | val.pop_back(); 14 | for (; c < '0' || c > '9'; c = val.back(), val.pop_back()) { 15 | if (c == '-') { 16 | f = 1; 17 | } 18 | } 19 | for (; c >= '0' && c <= '9'; c = val.back(), val.pop_back()) { 20 | ans = ans * 10 + c - '0'; 21 | } 22 | j = f ? -ans : ans; 23 | return it; 24 | } 25 | auto &operator<<(ostream &os, const i64 &j) { 26 | string ans; 27 | function write = [&](i64 x) { 28 | if (x < 0) ans += '-', x = -x; 29 | if (x > 9) write(x / 10); 30 | ans += x % 10 + '0'; 31 | }; 32 | write(j); 33 | return os << ans; 34 | } 35 | } // namespace my128 36 | 37 | using namespace my128; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/01 - 初始与杂类/08 - 样例构造(随机数、树、图生成).cpp: -------------------------------------------------------------------------------- 1 | mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count()); 2 | int r(int a, int b) { 3 | return rnd() % (b - a + 1) + a; 4 | } 5 | 6 | void graph(int n, int root = -1, int m = -1) { 7 | vector> t; 8 | for (int i = 1; i < n; i++) { // 先建立一棵以0为根节点的树 9 | t.emplace_back(i, r(0, i - 1)); 10 | } 11 | 12 | vector> edge; 13 | set> uni; 14 | if (root == -1) { 15 | root = r(0, n - 1); // 确定根节点 16 | } 17 | for (auto [x, y] : t) { // 偏移建树 18 | x = (x + root) % n + 1; 19 | y = (y + root) % n + 1; 20 | edge.emplace_back(x, y); 21 | uni.emplace(x, y); 22 | } 23 | 24 | if (m != -1) { // 如果是图,则在树的基础上继续加边 25 | for (int i = n; i <= m; i++) { 26 | while (true) { 27 | int x = r(1, n), y = r(1, n); 28 | if (x == y) continue; // 拒绝自环 29 | if (uni.count({x, y}) || uni.count({y, x})) continue; // 拒绝重边 30 | edge.emplace_back(x, y); 31 | uni.emplace(x, y); 32 | break; 33 | } 34 | } 35 | } 36 | 37 | random_shuffle(edge.begin(), edge.end()); // 打乱节点 38 | for (auto [x, y] : edge) { 39 | cout << x << " " << y << endl; 40 | } 41 | } -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/01 - 树的直径.cpp: -------------------------------------------------------------------------------- 1 | struct Tree { 2 | int n; 3 | vector> ver; 4 | Tree(int n) { 5 | this->n = n; 6 | ver.resize(n + 1); 7 | } 8 | void add(int x, int y) { 9 | ver[x].push_back(y); 10 | ver[y].push_back(x); 11 | } 12 | int getlen(int root) { // 获取x所在树的直径 13 | map dep; // map用于优化输入为森林时的深度计算,亦可用vector 14 | function dfs = [&](int x, int fa) -> void { 15 | for (auto y : ver[x]) { 16 | if (y == fa) continue; 17 | dep[y] = dep[x] + 1; 18 | dfs(y, x); 19 | } 20 | if (dep[x] > dep[root]) { 21 | root = x; 22 | } 23 | }; 24 | dfs(root, 0); 25 | int st = root; // 记录直径端点 26 | 27 | dep.clear(); 28 | dfs(root, 0); 29 | int ed = root; // 记录直径另一端点 30 | 31 | return dep[root]; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/02 - 点分治.cpp: -------------------------------------------------------------------------------- 1 | int root = 0, MaxTree = 1e18; //分别代表重心下标、最大子树大小 2 | vector vis(n + 1), siz(n + 1); 3 | auto get = [&](auto self, int x, int fa, int n) -> void { // 获取树的重心 4 | siz[x] = 1; 5 | int val = 0; 6 | for (auto [y, w] : ver[x]) { 7 | if (y == fa || vis[y]) continue; 8 | self(self, y, x, n); 9 | siz[x] += siz[y]; 10 | val = max(val, siz[y]); 11 | } 12 | val = max(val, n - siz[x]); 13 | if (val < MaxTree) { 14 | MaxTree = val; 15 | root = x; 16 | } 17 | }; 18 | 19 | auto clac = [&](int x) -> void { // 以 x 为新的根,维护询问 20 | set pre = {0}; // 记录到根节点 x 距离为 i 的路径是否存在 21 | vector dis(n + 1); 22 | for (auto [y, w] : ver[x]) { 23 | if (vis[y]) continue; 24 | vector child; // 记录 x 的子树节点的深度信息 25 | auto dfs = [&](auto self, int x, int fa) -> void { 26 | child.push_back(dis[x]); 27 | for (auto [y, w] : ver[x]) { 28 | if (y == fa || vis[y]) continue; 29 | dis[y] = dis[x] + w; 30 | self(self, y, x); 31 | } 32 | }; 33 | dis[y] = w; 34 | dfs(dfs, y, x); 35 | 36 | for (auto it : child) { 37 | for (int i = 1; i <= m; i++) { // 根据询问更新值 38 | if (q[i] < it || !pre.count(q[i] - it)) continue; 39 | ans[i] = 1; 40 | } 41 | } 42 | pre.insert(child.begin(), child.end()); 43 | } 44 | }; 45 | 46 | auto dfz = [&](auto self, int x, int fa) -> void { // 点分治 47 | vis[x] = 1; // 标记已经被更新过的旧重心,确保只对子树分治 48 | clac(x); 49 | for (auto [y, w] : ver[x]) { 50 | if (y == fa || vis[y]) continue; 51 | MaxTree = 1e18; 52 | get(get, y, x, siz[y]); 53 | self(self, root, x); 54 | } 55 | }; 56 | 57 | get(get, 1, 0, n); 58 | dfz(dfz, root, 0); 59 | -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/03 - HLD.cpp: -------------------------------------------------------------------------------- 1 | struct HLD { 2 | int n, idx; 3 | vector> ver; 4 | vector siz, dep; 5 | vector top, son, parent; 6 | vector in, id, val; 7 | Segt segt; 8 | 9 | HLD(int n) { 10 | this->n = n; 11 | ver.resize(n + 1); 12 | siz.resize(n + 1); 13 | dep.resize(n + 1); 14 | 15 | top.resize(n + 1); 16 | son.resize(n + 1); 17 | parent.resize(n + 1); 18 | 19 | idx = 0; 20 | in.resize(n + 1); 21 | id.resize(n + 1); 22 | val.resize(n + 1); 23 | } 24 | void add(int x, int y) { // 建立双向边 25 | ver[x].push_back(y); 26 | ver[y].push_back(x); 27 | } 28 | void dfs1(int x) { 29 | siz[x] = 1; 30 | dep[x] = dep[parent[x]] + 1; 31 | for (auto y : ver[x]) { 32 | if (y == parent[x]) continue; 33 | parent[y] = x; 34 | dfs1(y); 35 | siz[x] += siz[y]; 36 | if (siz[y] > siz[son[x]]) { 37 | son[x] = y; 38 | } 39 | } 40 | } 41 | void dfs2(int x, int up) { 42 | id[x] = ++idx; 43 | val[idx] = in[x]; // 建立编号 44 | top[x] = up; 45 | if (son[x]) dfs2(son[x], up); 46 | for (auto y : ver[x]) { 47 | if (y == parent[x] || y == son[x]) continue; 48 | dfs2(y, y); 49 | } 50 | } 51 | void modify(int l, int r, int val) { // 链上修改 52 | while (top[l] != top[r]) { 53 | if (dep[top[l]] < dep[top[r]]) { 54 | swap(l, r); 55 | } 56 | segt.modify(id[top[l]], id[l], val); 57 | l = parent[top[l]]; 58 | } 59 | if (dep[l] > dep[r]) { 60 | swap(l, r); 61 | } 62 | segt.modify(id[l], id[r], val); 63 | } 64 | void modify(int root, int val) { // 子树修改 65 | segt.modify(id[root], id[root] + siz[root] - 1, val); 66 | } 67 | int ask(int l, int r) { // 链上查询 68 | int ans = 0; 69 | while (top[l] != top[r]) { 70 | if (dep[top[l]] < dep[top[r]]) { 71 | swap(l, r); 72 | } 73 | ans += segt.ask(id[top[l]], id[l]); 74 | l = parent[top[l]]; 75 | } 76 | if (dep[l] > dep[r]) { 77 | swap(l, r); 78 | } 79 | return ans + segt.ask(id[l], id[r]); 80 | } 81 | int ask(int root) { // 子树查询 82 | return segt.ask(id[root], id[root] + siz[root] - 1); 83 | } 84 | void work(auto in, int root = 1) { // 在此初始化 85 | assert(in.size() == n + 1); 86 | this->in = in; 87 | dfs1(root); 88 | dfs2(root, root); 89 | segt.init(val); // 建立线段树 90 | } 91 | void work(int root = 1) { // 在此初始化 92 | dfs1(root); 93 | dfs2(root, root); 94 | segt.init(val); // 建立线段树 95 | } 96 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/04 - LCA(无权图树上倍增解).cpp: -------------------------------------------------------------------------------- 1 | struct Tree { 2 | int n; 3 | vector> ver, val; 4 | vector lg, dep; 5 | Tree(int n) { 6 | this->n = n; 7 | ver.resize(n + 1); 8 | val.resize(n + 1, vector(30)); 9 | lg.resize(n + 1); 10 | dep.resize(n + 1); 11 | for (int i = 1; i <= n; i++) { //预处理 log 12 | lg[i] = lg[i - 1] + (1 << lg[i - 1] == i); 13 | } 14 | } 15 | void add(int x, int y) { // 建立双向边 16 | ver[x].push_back(y); 17 | ver[y].push_back(x); 18 | } 19 | void dfs(int x, int fa) { 20 | val[x][0] = fa; // 储存 x 的父节点 21 | dep[x] = dep[fa] + 1; 22 | for (int i = 1; i <= lg[dep[x]]; i++) { 23 | val[x][i] = val[val[x][i - 1]][i - 1]; 24 | } 25 | for (auto y : ver[x]) { 26 | if (y == fa) continue; 27 | dfs(y, x); 28 | } 29 | } 30 | int lca(int x, int y) { 31 | if (dep[x] < dep[y]) swap(x, y); 32 | while (dep[x] > dep[y]) { 33 | x = val[x][lg[dep[x] - dep[y]] - 1]; 34 | } 35 | if (x == y) return x; 36 | for (int k = lg[dep[x]] - 1; k >= 0; k--) { 37 | if (val[x][k] == val[y][k]) continue; 38 | x = val[x][k]; 39 | y = val[y][k]; 40 | } 41 | return val[x][0]; 42 | } 43 | int clac(int x, int y) { // 倍增查询两点间距离 44 | return dep[x] + dep[y] - 2 * dep[lca(x, y)]; 45 | } 46 | void work(int root = 1) { // 在此初始化 47 | dfs(root, 0); 48 | } 49 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/04 - LCA(有权图树上倍增解).cpp: -------------------------------------------------------------------------------- 1 | struct Tree { 2 | int n; 3 | vector> val, Max; 4 | vector>> ver; 5 | vector lg, dep; 6 | Tree(int n) { 7 | this->n = n; 8 | ver.resize(n + 1); 9 | val.resize(n + 1, vector(30)); 10 | Max.resize(n + 1, vector(30)); 11 | lg.resize(n + 1); 12 | dep.resize(n + 1); 13 | for (int i = 1; i <= n; i++) { //预处理 log 14 | lg[i] = lg[i - 1] + (1 << lg[i - 1] == i); 15 | } 16 | } 17 | void add(int x, int y, int w) { // 建立双向边 18 | ver[x].push_back({y, w}); 19 | ver[y].push_back({x, w}); 20 | } 21 | void dfs(int x, int fa) { 22 | val[x][0] = fa; 23 | dep[x] = dep[fa] + 1; 24 | for (int i = 1; i <= lg[dep[x]]; i++) { 25 | val[x][i] = val[val[x][i - 1]][i - 1]; 26 | Max[x][i] = max(Max[x][i - 1], Max[val[x][i - 1]][i - 1]); 27 | } 28 | for (auto [y, w] : ver[x]) { 29 | if (y == fa) continue; 30 | Max[y][0] = w; 31 | dfs(y, x); 32 | } 33 | } 34 | int lca(int x, int y) { 35 | if (dep[x] < dep[y]) swap(x, y); 36 | while (dep[x] > dep[y]) { 37 | x = val[x][lg[dep[x] - dep[y]] - 1]; 38 | } 39 | if (x == y) return x; 40 | for (int k = lg[dep[x]] - 1; k >= 0; k--) { 41 | if (val[x][k] == val[y][k]) continue; 42 | x = val[x][k]; 43 | y = val[y][k]; 44 | } 45 | return val[x][0]; 46 | } 47 | int clac(int x, int y) { // 倍增查询两点间距离 48 | return dep[x] + dep[y] - 2 * dep[lca(x, y)]; 49 | } 50 | int query(int x, int y) { // 倍增查询两点路径上的最大边权(带权图) 51 | auto get = [&](int x, int y) -> int { 52 | int ans = 0; 53 | if (x == y) return ans; 54 | for (int i = lg[dep[x]]; i >= 0; i--) { 55 | if (dep[val[x][i]] > dep[y]) { 56 | ans = max(ans, Max[x][i]); 57 | x = val[x][i]; 58 | } 59 | } 60 | ans = max(ans, Max[x][0]); 61 | return ans; 62 | }; 63 | int fa = lca(x, y); 64 | return max(get(x, fa), get(y, fa)); 65 | } 66 | void work(int root = 1) { // 在此初始化 67 | dfs(root, 0); 68 | } 69 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/04 - LCA(树链剖分解).cpp: -------------------------------------------------------------------------------- 1 | struct HLD { 2 | int n, idx; 3 | vector> ver; 4 | vector siz, dep; 5 | vector top, son, parent; 6 | 7 | HLD(int n) { 8 | this->n = n; 9 | ver.resize(n + 1); 10 | siz.resize(n + 1); 11 | dep.resize(n + 1); 12 | 13 | top.resize(n + 1); 14 | son.resize(n + 1); 15 | parent.resize(n + 1); 16 | } 17 | void add(int x, int y) { // 建立双向边 18 | ver[x].push_back(y); 19 | ver[y].push_back(x); 20 | } 21 | void dfs1(int x) { 22 | siz[x] = 1; 23 | dep[x] = dep[parent[x]] + 1; 24 | for (auto y : ver[x]) { 25 | if (y == parent[x]) continue; 26 | parent[y] = x; 27 | dfs1(y); 28 | siz[x] += siz[y]; 29 | if (siz[y] > siz[son[x]]) { 30 | son[x] = y; 31 | } 32 | } 33 | } 34 | void dfs2(int x, int up) { 35 | top[x] = up; 36 | if (son[x]) dfs2(son[x], up); 37 | for (auto y : ver[x]) { 38 | if (y == parent[x] || y == son[x]) continue; 39 | dfs2(y, y); 40 | } 41 | } 42 | int lca(int x, int y) { 43 | while (top[x] != top[y]) { 44 | if (dep[top[x]] > dep[top[y]]) { 45 | x = parent[top[x]]; 46 | } else { 47 | y = parent[top[y]]; 48 | } 49 | } 50 | return dep[x] < dep[y] ? x : y; 51 | } 52 | int clac(int x, int y) { // 查询两点间距离 53 | return dep[x] + dep[y] - 2 * dep[lca(x, y)]; 54 | } 55 | void work(int root = 1) { // 在此初始化 56 | dfs1(root); 57 | dfs2(root, root); 58 | } 59 | }; 60 | -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/05 - Djikstra.cpp: -------------------------------------------------------------------------------- 1 | vector dis(n + 1, 1E18); 2 | auto djikstra = [&](int s = 1) -> void { 3 | using PII = pair; 4 | priority_queue, greater> q; 5 | q.emplace(0, s); 6 | dis[s] = 0; 7 | vector vis(n + 1); 8 | while (!q.empty()) { 9 | int x = q.top().second; 10 | q.pop(); 11 | if (vis[x]) continue; 12 | vis[x] = 1; 13 | for (auto [y, w] : ver[x]) { 14 | if (dis[y] > dis[x] + w) { 15 | dis[y] = dis[x] + w; 16 | q.emplace(dis[y], y); 17 | } 18 | } 19 | } 20 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/06 - Floyd.cpp: -------------------------------------------------------------------------------- 1 | const int N = 210; 2 | int n, m, d[N][N]; 3 | 4 | void floyd() { 5 | for (int k = 1; k <= n; k ++) 6 | for (int i = 1; i <= n; i ++) 7 | for (int j = 1; j <= n; j ++) 8 | d[i][j] = min(d[i][j], d[i][k] + d[k][j]); 9 | } 10 | int main() { 11 | cin >> n >> m; 12 | for (int i = 1; i <= n; i ++) 13 | for (int j = 1; j <= n; j ++) 14 | if (i == j) d[i][j] = 0; 15 | else d[i][j] = INF; 16 | while (m --) { 17 | int x, y, w; cin >> x >> y >> w; 18 | d[x][y] = min(d[x][y], w); 19 | } 20 | floyd(); 21 | for (int i = 1; i <= n; ++ i) { 22 | for (int j = 1; j <= n; ++ j) { 23 | if (d[i][j] > INF / 2) cout << "N" << endl; 24 | else cout << d[i][j] << endl; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/07 - MST (Kruskal).cpp: -------------------------------------------------------------------------------- 1 | struct DSU { 2 | vector fa; 3 | DSU(int n) : fa(n + 1) { 4 | iota(fa.begin(), fa.end(), 0); 5 | } 6 | int get(int x) { 7 | while (x != fa[x]) { 8 | x = fa[x] = fa[fa[x]]; 9 | } 10 | return x; 11 | } 12 | bool merge(int x, int y) { // 设x是y的祖先 13 | x = get(x), y = get(y); 14 | if (x == y) return false; 15 | fa[y] = x; 16 | return true; 17 | } 18 | bool same(int x, int y) { 19 | return get(x) == get(y); 20 | } 21 | }; 22 | struct Tree { 23 | using TII = tuple; 24 | int n; 25 | priority_queue, greater> ver; 26 | 27 | Tree(int n) { 28 | this->n = n; 29 | } 30 | void add(int x, int y, int w) { 31 | ver.emplace(w, x, y); // 注意顺序 32 | } 33 | int kruskal() { 34 | DSU dsu(n); 35 | int ans = 0, cnt = 0; 36 | while (ver.size()) { 37 | auto [w, x, y] = ver.top(); 38 | ver.pop(); 39 | if (dsu.same(x, y)) continue; 40 | dsu.merge(x, y); 41 | ans += w; 42 | cnt++; 43 | } 44 | assert(cnt < n - 1); // 输入有误,建树失败 45 | return ans; 46 | } 47 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/08 - SCC (Tarjan).cpp: -------------------------------------------------------------------------------- 1 | struct SCC { 2 | int n, now, cnt; 3 | vector> ver; 4 | vector dfn, low, col, S; 5 | 6 | SCC(int n) : n(n), ver(n + 1), low(n + 1) { 7 | dfn.resize(n + 1, -1); 8 | col.resize(n + 1, -1); 9 | now = cnt = 0; 10 | } 11 | void add(int x, int y) { 12 | ver[x].push_back(y); 13 | } 14 | void tarjan(int x) { 15 | dfn[x] = low[x] = now++; 16 | S.push_back(x); 17 | for (auto y : ver[x]) { 18 | if (dfn[y] == -1) { 19 | tarjan(y); 20 | low[x] = min(low[x], low[y]); 21 | } else if (col[y] == -1) { 22 | low[x] = min(low[x], dfn[y]); 23 | } 24 | } 25 | if (dfn[x] == low[x]) { 26 | int pre; 27 | cnt++; 28 | do { 29 | pre = S.back(); 30 | col[pre] = cnt; 31 | S.pop_back(); 32 | } while (pre != x); 33 | } 34 | } 35 | auto work() { // [cnt 新图的顶点数量] 36 | for (int i = 1; i <= n; i++) { // 避免图不连通 37 | if (dfn[i] == -1) { 38 | tarjan(i); 39 | } 40 | } 41 | 42 | vector siz(cnt + 1); // siz 每个 scc 中点的数量 43 | vector> adj(cnt + 1); 44 | for (int i = 1; i <= n; i++) { 45 | siz[col[i]]++; 46 | for (auto j : ver[i]) { 47 | int x = col[i], y = col[j]; 48 | if (x != y) { 49 | adj[x].push_back(y); 50 | } 51 | } 52 | } 53 | return {cnt, adj, col, siz}; 54 | } 55 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/09 - EDCC (Tarjan)(新版).cpp: -------------------------------------------------------------------------------- 1 | struct EDCC { 2 | int n, now, cnt; 3 | vector> ver; 4 | vector dfn, low, col, S; 5 | set> bridge; 6 | 7 | EDCC(int n) : n(n), ver(n + 1), low(n + 1) { 8 | dfn.resize(n + 1, -1); 9 | col.resize(n + 1, -1); 10 | now = cnt = 0; 11 | } 12 | void add(int x, int y) { 13 | ver[x].push_back(y); 14 | ver[y].push_back(x); 15 | } 16 | void tarjan(int x, int fa) { 17 | dfn[x] = low[x] = now++; 18 | S.push_back(x); 19 | for (auto y : ver[x]) { 20 | if (y == fa) continue; 21 | if (dfn[y] == -1) { 22 | bridge.insert({x, y}); 23 | tarjan(y, x); 24 | low[x] = min(low[x], low[y]); 25 | } else if (col[y] == -1 && dfn[y] < dfn[x]) { 26 | bridge.insert({x, y}); 27 | low[x] = min(low[x], dfn[y]); 28 | } 29 | } 30 | if (dfn[x] == low[x]) { 31 | int pre; 32 | cnt++; 33 | do { 34 | pre = S.back(); 35 | col[pre] = cnt; 36 | S.pop_back(); 37 | } while (pre != x); 38 | } 39 | } 40 | auto work() { // [cnt 新图的顶点数量, bridge 全部割边] 41 | for (int i = 1; i <= n; i++) { // 避免图不连通 42 | if (dfn[i] == -1) { 43 | tarjan(i, 0); 44 | } 45 | } 46 | 47 | vector siz(cnt + 1); // siz 每个边双中点的数量 48 | vector> adj(cnt + 1); // adj 新图 49 | for (int i = 1; i <= n; i++) { 50 | siz[col[i]]++; 51 | for (auto j : ver[i]) { 52 | int x = col[i], y = col[j]; 53 | if (x != y) { 54 | adj[x].push_back(y); 55 | } 56 | } 57 | } 58 | return tuple{cnt, adj, col, siz}; 59 | } 60 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/09 - EDCC (Tarjan)(链式前向星旧版,yxc版本).cpp: -------------------------------------------------------------------------------- 1 | struct E_DCC { 2 | int n; 3 | vector h, ver, ne; 4 | vector dfn, low, col, S; 5 | int now, cnt, tot; 6 | vector bridge; // 记录是否是割边 7 | 8 | E_DCC(int n, int m) : n(n) { 9 | m *= 2; // 注意链式前向星边的数量翻倍 10 | ver.resize(m + 1); 11 | ne.resize(m + 1); 12 | bridge.resize(m + 1); 13 | 14 | h.resize(n + 1, -1); 15 | dfn.resize(n + 1); 16 | low.resize(n + 1); 17 | col.resize(n + 1); 18 | S.clear(); 19 | tot = cnt = now = 0; 20 | } 21 | void add(int x, int y) { // 注意,这里的编号从 0 开始 22 | ver[tot] = y, ne[tot] = h[x], h[x] = tot++; 23 | ver[tot] = x, ne[tot] = h[y], h[y] = tot++; 24 | } 25 | void tarjan(int x, int fa) { // 这里是缩边双,不是缩点,不相同 26 | dfn[x] = low[x] = ++now; 27 | S.push_back(x); 28 | for (int i = h[x]; ~i; i = ne[i]) { 29 | int y = ver[i]; 30 | if (!dfn[y]) { 31 | tarjan(y, i); // 这里储存的是父亲边的编号 32 | low[x] = min(low[x], low[y]); 33 | // y 不能到达 x 的任何一个祖先节点,(x - y) 即为一条割边 34 | // 但是在这里,我们不直接储存 (x - y) 这条边,而是储存边的编号 35 | // 这样做是为了处理重边的情况(点可能相同,但是边的编号绝对不相同) 36 | if (dfn[x] < low[y]) { 37 | bridge[i] = bridge[i ^ 1] = true; 38 | } 39 | } else if (i != (fa ^ 1)) { // 这里同样的,使用边的编号来处理重边情况 40 | low[x] = min(low[x], dfn[y]); 41 | } 42 | } 43 | if (dfn[x] == low[x]) { 44 | int pre = 0; 45 | cnt++; 46 | do { 47 | pre = S.back(); 48 | S.pop_back(); 49 | col[pre] = cnt; 50 | } while (pre != x); 51 | } 52 | } 53 | pair>> rebuild() { // [新图的顶点数量, 新图] 54 | work(); 55 | vector> adj(cnt + 1); 56 | for (int i = 0; i < tot; ++i) { 57 | if (bridge[i]) { // 如果 (i, i ^ 1) 是割边 58 | int x = col[ver[i]], y = col[ver[i ^ 1]]; 59 | adj[x].push_back(y); // 割边两端点颜色必定不同,故直接连边 60 | } 61 | } 62 | return {cnt, adj}; 63 | } 64 | void work() { 65 | for (int i = 1; i <= n; i++) { // 避免图不连通 66 | if (dfn[i] == 0) { 67 | tarjan(i, -1); 68 | } 69 | } 70 | } 71 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/10 - VDCC (Tarjan).cpp: -------------------------------------------------------------------------------- 1 | struct V_DCC { 2 | int n; 3 | vector> ver, col; 4 | vector dfn, low, S; 5 | int now, cnt; 6 | vector point; // 记录是否为割点 7 | 8 | V_DCC(int n) : n(n) { 9 | ver.resize(n + 1); 10 | dfn.resize(n + 1); 11 | low.resize(n + 1); 12 | col.resize(2 * n + 1); 13 | point.resize(n + 1); 14 | S.clear(); 15 | cnt = now = 0; 16 | } 17 | void add(int x, int y) { 18 | if (x == y) return; // 手动去除重边 19 | ver[x].push_back(y); 20 | ver[y].push_back(x); 21 | } 22 | void tarjan(int x, int root) { 23 | low[x] = dfn[x] = now++; 24 | S.push_back(x); 25 | if (x == root && !ver[x].size()) { // 特判孤立点 26 | ++cnt; 27 | col[cnt].push_back(x); 28 | return; 29 | } 30 | 31 | int flag = 0; 32 | for (auto y : ver[x]) { 33 | if (!dfn[y]) { 34 | tarjan(y, root); 35 | low[x] = min(low[x], low[y]); 36 | if (dfn[x] <= low[y]) { 37 | flag++; 38 | if (x != root || flag > 1) { 39 | point[x] = true; // 标记为割点 40 | } 41 | int pre = 0; 42 | cnt++; 43 | do { 44 | pre = S.back(); 45 | col[cnt].push_back(pre); 46 | S.pop_back(); 47 | } while (pre != y); 48 | col[cnt].push_back(x); 49 | } 50 | } else { 51 | low[x] = min(low[x], dfn[y]); 52 | } 53 | } 54 | } 55 | pair>> rebuild() { // [新图的顶点数量, 新图] 56 | work(); 57 | vector> adj(cnt + 1); 58 | for (int i = 1; i <= cnt; i++) { 59 | if (!col[i].size()) { // 注意,孤立点也是 V-DCC 60 | continue; 61 | } 62 | for (auto j : col[i]) { 63 | if (point[j]) { // 如果 j 是割点 64 | adj[i].push_back(point[j]); 65 | adj[point[j]].push_back(i); 66 | } 67 | } 68 | } 69 | return {cnt, adj}; 70 | } 71 | void work() { 72 | for (int i = 1; i <= n; ++i) { // 避免图不连通 73 | if (!dfn[i]) { 74 | tarjan(i, i); 75 | } 76 | } 77 | } 78 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/13 - 二分图最大匹配 (HopcroftKarp).cpp: -------------------------------------------------------------------------------- 1 | struct HopcroftKarp { 2 | vector> g; 3 | vector pa, pb, vis; 4 | int n, m, dfn, res; 5 | 6 | HopcroftKarp(int _n, int _m) : n(_n + 1), m(_m + 1) { 7 | assert(0 <= n && 0 <= m); 8 | pa.assign(n, -1); 9 | pb.assign(m, -1); 10 | vis.resize(n); 11 | g.resize(n); 12 | res = 0; 13 | dfn = 0; 14 | } 15 | void add(int x, int y) { 16 | assert(0 <= x && x < n && 0 <= y && y < m); 17 | g[x].push_back(y); 18 | } 19 | bool dfs(int v) { 20 | vis[v] = dfn; 21 | for (int u : g[v]) { 22 | if (pb[u] == -1) { 23 | pb[u] = v; 24 | pa[v] = u; 25 | return true; 26 | } 27 | } 28 | for (int u : g[v]) { 29 | if (vis[pb[u]] != dfn && dfs(pb[u])) { 30 | pa[v] = u; 31 | pb[u] = v; 32 | return true; 33 | } 34 | } 35 | return false; 36 | } 37 | int work() { 38 | while (1) { 39 | dfn++; 40 | int cnt = 0; 41 | for (int i = 0; i < n; i++) { 42 | if (pa[i] == -1 && dfs(i)) { 43 | cnt++; 44 | } 45 | } 46 | if (cnt == 0) break; 47 | res += cnt; 48 | } 49 | return res; 50 | } 51 | }; 52 | signed main() { 53 | int n1, n2, m; 54 | cin >> n1 >> n2 >> m; 55 | HopcroftKarp flow(n1, n2); 56 | while (m--) { 57 | int x, y; 58 | cin >> x >> y; 59 | flow.add(x, y); 60 | } 61 | cout << flow.work() << endl; 62 | } -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/14 - 2-Sat.cpp: -------------------------------------------------------------------------------- 1 | struct TwoSat { 2 | int n; 3 | vector> e; 4 | vector ans; 5 | TwoSat(int n) : n(n), e(2 * n), ans(n) {} 6 | void add(int u, bool f, int v, bool g) { 7 | e[2 * u + !f].push_back(2 * v + g); 8 | e[2 * v + !g].push_back(2 * u + f); 9 | } 10 | bool work() { 11 | vector id(2 * n, -1), dfn(2 * n, -1), low(2 * n, -1); 12 | vector stk; 13 | int now = 0, cnt = 0; 14 | auto tarjan = [&](auto self, int u) -> void { 15 | stk.push_back(u); 16 | dfn[u] = low[u] = now++; 17 | for (auto v : e[u]) { 18 | if (dfn[v] == -1) { 19 | self(self, v); 20 | low[u] = min(low[u], low[v]); 21 | } else if (id[v] == -1) { 22 | low[u] = min(low[u], dfn[v]); 23 | } 24 | } 25 | if (dfn[u] == low[u]) { 26 | int v; 27 | do { 28 | v = stk.back(); 29 | stk.pop_back(); 30 | id[v] = cnt; 31 | } while (v != u); 32 | ++cnt; 33 | } 34 | }; 35 | for (int i = 0; i < 2 * n; ++i) { 36 | if (dfn[i] == -1) { 37 | tarjan(tarjan, i); 38 | } 39 | } 40 | for (int i = 0; i < n; ++i) { 41 | if (id[2 * i] == id[2 * i + 1]) return false; 42 | ans[i] = id[2 * i] > id[2 * i + 1]; 43 | } 44 | return true; 45 | } 46 | vector answer() { 47 | return ans; 48 | } 49 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/15 - 二分图最大权匹配 (KM).cpp: -------------------------------------------------------------------------------- 1 | struct MaxCostMatch { 2 | vector ansl, ansr, pre; 3 | vector lx, ly; 4 | vector> ver; 5 | int n; 6 | 7 | MaxCostMatch(int n) : n(n) { 8 | ver.resize(n + 1, vector(n + 1)); 9 | ansl.resize(n + 1, -1); 10 | ansr.resize(n + 1, -1); 11 | lx.resize(n + 1); 12 | ly.resize(n + 1, -1E18); 13 | pre.resize(n + 1); 14 | } 15 | void add(int x, int y, int w) { 16 | ver[x][y] = w; 17 | } 18 | void bfs(int x) { 19 | vector visl(n + 1), visr(n + 1); 20 | vector slack(n + 1, 1E18); 21 | queue q; 22 | function check = [&](int x) { 23 | visr[x] = 1; 24 | if (~ansr[x]) { 25 | q.push(ansr[x]); 26 | visl[ansr[x]] = 1; 27 | return false; 28 | } 29 | while (~x) { 30 | ansr[x] = pre[x]; 31 | swap(x, ansl[pre[x]]); 32 | } 33 | return true; 34 | }; 35 | q.push(x); 36 | visl[x] = 1; 37 | while (1) { 38 | while (!q.empty()) { 39 | int x = q.front(); 40 | q.pop(); 41 | for (int y = 1; y <= n; ++y) { 42 | if (visr[y]) continue; 43 | int del = lx[x] + ly[y] - ver[x][y]; 44 | if (del < slack[y]) { 45 | pre[y] = x; 46 | slack[y] = del; 47 | if (!slack[y] && check(y)) return; 48 | } 49 | } 50 | } 51 | int val = 1E18; 52 | for (int i = 1; i <= n; ++i) { 53 | if (!visr[i]) { 54 | val = min(val, slack[i]); 55 | } 56 | } 57 | for (int i = 1; i <= n; ++i) { 58 | if (visl[i]) lx[i] -= val; 59 | if (visr[i]) { 60 | ly[i] += val; 61 | } else { 62 | slack[i] -= val; 63 | } 64 | } 65 | for (int i = 1; i <= n; ++i) { 66 | if (!visr[i] && !slack[i] && check(i)) { 67 | return; 68 | } 69 | } 70 | } 71 | } 72 | int work() { 73 | for (int i = 1; i <= n; ++i) { 74 | for (int j = 1; j <= n; ++j) { 75 | ly[i] = max(ly[i], ver[j][i]); 76 | } 77 | } 78 | for (int i = 1; i <= n; ++i) bfs(i); 79 | int res = 0; 80 | for (int i = 1; i <= n; ++i) { 81 | res += ver[i][ansl[i]]; 82 | } 83 | return res; 84 | } 85 | void getMatch(int x, int y) { // 获取方案 (0代表无匹配) 86 | for (int i = 1; i <= x; ++i) { 87 | cout << (ver[i][ansl[i]] ? ansl[i] : 0) << " "; 88 | } 89 | cout << endl; 90 | for (int i = 1; i <= y; ++i) { 91 | cout << (ver[i][ansr[i]] ? ansr[i] : 0) << " "; 92 | } 93 | cout << endl; 94 | } 95 | }; 96 | 97 | signed main() { 98 | int n1, n2, m; 99 | cin >> n1 >> n2 >> m; 100 | 101 | MaxCostMatch match(max(n1, n2)); 102 | for (int i = 1; i <= m; i++) { 103 | int x, y, w; 104 | cin >> x >> y >> w; 105 | match.add(x, y, w); 106 | } 107 | cout << match.work() << '\n'; 108 | match.getMatch(n1, n2); 109 | } -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/16 - 最大流 (Dinic).cpp: -------------------------------------------------------------------------------- 1 | template struct Flow_ { 2 | const int n; 3 | const T inf = numeric_limits::max(); 4 | struct Edge { 5 | int to; 6 | T w; 7 | Edge(int to, T w) : to(to), w(w) {} 8 | }; 9 | vector ver; 10 | vector> h; 11 | vector cur, d; 12 | 13 | Flow_(int n) : n(n + 1), h(n + 1) {} 14 | void add(int u, int v, T c) { 15 | h[u].push_back(ver.size()); 16 | ver.emplace_back(v, c); 17 | h[v].push_back(ver.size()); 18 | ver.emplace_back(u, 0); 19 | } 20 | bool bfs(int s, int t) { 21 | d.assign(n, -1); 22 | d[s] = 0; 23 | queue q; 24 | q.push(s); 25 | while (!q.empty()) { 26 | auto x = q.front(); 27 | q.pop(); 28 | for (auto it : h[x]) { 29 | auto [y, w] = ver[it]; 30 | if (w && d[y] == -1) { 31 | d[y] = d[x] + 1; 32 | if (y == t) return true; 33 | q.push(y); 34 | } 35 | } 36 | } 37 | return false; 38 | } 39 | T dfs(int u, int t, T f) { 40 | if (u == t) return f; 41 | auto r = f; 42 | for (int &i = cur[u]; i < h[u].size(); i++) { 43 | auto j = h[u][i]; 44 | auto &[v, c] = ver[j]; 45 | auto &[u, rc] = ver[j ^ 1]; 46 | if (c && d[v] == d[u] + 1) { 47 | auto a = dfs(v, t, std::min(r, c)); 48 | c -= a; 49 | rc += a; 50 | r -= a; 51 | if (!r) return f; 52 | } 53 | } 54 | return f - r; 55 | } 56 | T work(int s, int t) { 57 | T ans = 0; 58 | while (bfs(s, t)) { 59 | cur.assign(n, 0); 60 | ans += dfs(s, t, inf); 61 | } 62 | return ans; 63 | } 64 | }; 65 | using Flow = Flow_; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/02 - 图、树、网络流/18 - 费用流 (Djikstra).cpp: -------------------------------------------------------------------------------- 1 | struct MinCostFlow { 2 | using LL = long long; 3 | using PII = pair; 4 | const LL INF = numeric_limits::max(); 5 | struct Edge { 6 | int v, c, f; 7 | Edge(int v, int c, int f) : v(v), c(c), f(f) {} 8 | }; 9 | const int n; 10 | vector e; 11 | vector> g; 12 | vector h, dis; 13 | vector pre; 14 | 15 | MinCostFlow(int n) : n(n), g(n) {} 16 | void add(int u, int v, int c, int f) { // c 流量, f 费用 17 | // if (f < 0) { 18 | // g[u].push_back(e.size()); 19 | // e.emplace_back(v, 0, f); 20 | // g[v].push_back(e.size()); 21 | // e.emplace_back(u, c, -f); 22 | // } else { 23 | g[u].push_back(e.size()); 24 | e.emplace_back(v, c, f); 25 | g[v].push_back(e.size()); 26 | e.emplace_back(u, 0, -f); 27 | // } 28 | } 29 | bool dijkstra(int s, int t) { 30 | dis.assign(n, INF); 31 | pre.assign(n, -1); 32 | priority_queue, greater> que; 33 | dis[s] = 0; 34 | que.emplace(0, s); 35 | while (!que.empty()) { 36 | auto [d, u] = que.top(); 37 | que.pop(); 38 | if (dis[u] < d) continue; 39 | for (int i : g[u]) { 40 | auto [v, c, f] = e[i]; 41 | if (c > 0 && dis[v] > d + h[u] - h[v] + f) { 42 | dis[v] = d + h[u] - h[v] + f; 43 | pre[v] = i; 44 | que.emplace(dis[v], v); 45 | } 46 | } 47 | } 48 | return dis[t] != INF; 49 | } 50 | pair flow(int s, int t) { 51 | int flow = 0; 52 | LL cost = 0; 53 | h.assign(n, 0); 54 | while (dijkstra(s, t)) { 55 | for (int i = 0; i < n; ++i) h[i] += dis[i]; 56 | int aug = numeric_limits::max(); 57 | for (int i = t; i != s; i = e[pre[i] ^ 1].v) aug = min(aug, e[pre[i]].c); 58 | for (int i = t; i != s; i = e[pre[i] ^ 1].v) { 59 | e[pre[i]].c -= aug; 60 | e[pre[i] ^ 1].c += aug; 61 | } 62 | flow += aug; 63 | cost += LL(aug) * h[t]; 64 | } 65 | return {flow, cost}; 66 | } 67 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/03 - 数论、几何/18 - 欧拉筛.cpp: -------------------------------------------------------------------------------- 1 | vector prime; // 这里储存筛出来的全部质数 2 | auto euler_Prime = [&](int n) -> void { 3 | vector v(n + 1); 4 | for (int i = 2; i <= n; ++i) { 5 | if (!v[i]) { 6 | v[i] = i; 7 | prime.push_back(i); 8 | } 9 | for (int j = 0; j < prime.size(); ++j) { 10 | if (prime[j] > v[i] || prime[j] > n / i) break; 11 | v[i * prime[j]] = prime[j]; 12 | } 13 | } 14 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/03 - 数论、几何/19 - Exgcd.cpp: -------------------------------------------------------------------------------- 1 | template T sign(const T &a) { 2 | return a == 0 ? 0 : (a < 0 ? -1 : 1); 3 | } 4 | template T ceil(const T &a, const T &b) { 5 | T A = abs(a), B = abs(b); 6 | assert(b != 0); 7 | return sign(a) * sign(b) > 0 ? (A + B - 1) / B : -A / B; 8 | } 9 | int exgcd(int a, int b, int &x, int &y) { 10 | if (!b) { 11 | x = 1, y = 0; 12 | return a; 13 | } 14 | int d = exgcd(b, a % b, y, x); 15 | y -= a / b * x; 16 | return d; 17 | } 18 | 19 | auto clac = [&](int a, int b, int c) -> void { 20 | int x, y, d = exgcd(a, b, x, y); 21 | if (c % d != 0) { 22 | cout << -1 << endl; 23 | return; 24 | } 25 | x *= c / d, y *= c / d; 26 | int p = b / d, q = a / d, k; 27 | if (x < 0) { 28 | k = ceil(1 - x, p); 29 | x += p * k; 30 | y -= q * k; 31 | } 32 | else if (x >= 0) { //将x提高到最小正整数 33 | k = (x - 1) / p; 34 | x -= p * k; //将x降低到最小正整数 35 | y += q * k; 36 | } 37 | if (y > 0) { //有正整数解 38 | cout << (y - 1) / q + 1 << endl; //将y减到1的方案数即为解的个数 39 | cout << x << endl; //当前的x即为最小正整数x 40 | cout << (y - 1) % q + 1 << endl; //将y取到最小正整数 41 | cout << x + (y - 1) / q * p << endl; //将x提升到最大 42 | cout << y << endl; //特解即为y最大值 43 | } else { //无整数解 44 | cout << x << endl; //当前的x即为最小的正整数x 45 | cout << y + q * ceil(1 - y, q) << endl; //将y提高到正整数 46 | } 47 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/03 - 数论、几何/20 - 组合数 (Lucas).cpp: -------------------------------------------------------------------------------- 1 | struct Comb { 2 | int n; 3 | vector _fac, _inv; 4 | 5 | Comb() : _fac{1}, _inv{0} {} 6 | Comb(int n) : Comb() { 7 | init(n); 8 | } 9 | void init(int m) { 10 | if (m <= n) return; 11 | _fac.resize(m + 1); 12 | _inv.resize(m + 1); 13 | for (int i = n + 1; i <= m; i++) { 14 | _fac[i] = _fac[i - 1] * i; 15 | } 16 | _inv[m] = _fac[m].inv(); 17 | for (int i = m; i > n; i--) { 18 | _inv[i - 1] = _inv[i] * i; 19 | } 20 | n = m; 21 | } 22 | Z fac(int x) { 23 | if (x > n) init(x); 24 | return _fac[x]; 25 | } 26 | Z inv(int x) { 27 | if (x > n) init(x); 28 | return _inv[x]; 29 | } 30 | Z C(int x, int y) { 31 | if (x < 0 || y < 0 || x < y) return 0; 32 | return fac(x) * inv(y) * inv(x - y); 33 | } 34 | Z P(int x, int y) { 35 | if (x < 0 || y < 0 || x < y) return 0; 36 | return fac(x) * inv(x - y); 37 | } 38 | } comb(1 << 21); -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/03 - 数论、几何/21 - Miller-Rabin + Pollard-Rho.cpp: -------------------------------------------------------------------------------- 1 | int mul(int a, int b, int m) { 2 | int r = a * b - m * (int)(1.L / m * a * b); 3 | return r - m * (r >= m) + m * (r < 0); 4 | } 5 | int mypow(int a, int b, int m) { 6 | int res = 1 % m; 7 | for (; b; b >>= 1, a = mul(a, a, m)) { 8 | if (b & 1) { 9 | res = mul(res, a, m); 10 | } 11 | } 12 | return res; 13 | } 14 | 15 | int B[] = {2, 3, 5, 7, 11, 13, 17, 19, 23}; 16 | bool MR(int n) { 17 | if (n <= 1) return 0; 18 | for (int p : B) { 19 | if (n == p) return 1; 20 | if (n % p == 0) return 0; 21 | } 22 | int m = (n - 1) >> __builtin_ctz(n - 1); 23 | for (int p : B) { 24 | int t = m, a = mypow(p, m, n); 25 | while (t != n - 1 && a != 1 && a != n - 1) { 26 | a = mul(a, a, n); 27 | t *= 2; 28 | } 29 | if (a != n - 1 && t % 2 == 0) return 0; 30 | } 31 | return 1; 32 | } 33 | int PR(int n) { 34 | for (int p : B) { 35 | if (n % p == 0) return p; 36 | } 37 | auto f = [&](int x) -> int { 38 | x = mul(x, x, n) + 1; 39 | return x >= n ? x - n : x; 40 | }; 41 | int x = 0, y = 0, tot = 0, p = 1, q, g; 42 | for (int i = 0; (i & 255) || (g = gcd(p, n)) == 1; i++, x = f(x), y = f(f(y))) { 43 | if (x == y) { 44 | x = tot++; 45 | y = f(x); 46 | } 47 | q = mul(p, abs(x - y), n); 48 | if (q) p = q; 49 | } 50 | return g; 51 | } 52 | vector fac(int n) { 53 | #define pb emplace_back 54 | if (n == 1) return {}; 55 | if (MR(n)) return {n}; 56 | int d = PR(n); 57 | auto v1 = fac(d), v2 = fac(n / d); 58 | auto i1 = v1.begin(), i2 = v2.begin(); 59 | vector ans; 60 | while (i1 != v1.end() || i2 != v2.end()) { 61 | if (i1 == v1.end()) { 62 | ans.pb(*i2++); 63 | } else if (i2 == v2.end()) { 64 | ans.pb(*i1++); 65 | } else { 66 | if (*i1 < *i2) { 67 | ans.pb(*i1++); 68 | } else { 69 | ans.pb(*i2++); 70 | } 71 | } 72 | } 73 | return ans; 74 | } -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/03 - 数论、几何/23 - 立体几何.cpp: -------------------------------------------------------------------------------- 1 | namespace Geometry { // 立体几何基础 2 | struct Point3 { 3 | ld x, y, z; 4 | Point3(ld x_ = 0, ld y_ = 0, ld z_ = 0) : x(x_), y(y_), z(z_) {} 5 | Point3 &operator+=(Point3 p) & { 6 | return x += p.x, y += p.y, z += p.z, *this; 7 | } 8 | Point3 &operator-=(Point3 p) & { 9 | return x -= p.x, y -= p.y, z -= p.z, *this; 10 | } 11 | Point3 &operator*=(Point3 p) & { 12 | return x *= p.x, y *= p.y, z *= p.z, *this; 13 | } 14 | Point3 &operator*=(ld t) & { 15 | return x *= t, y *= t, z *= t, *this; 16 | } 17 | Point3 &operator/=(ld t) & { 18 | return x /= t, y /= t, z /= t, *this; 19 | } 20 | friend Point3 operator+(Point3 a, Point3 b) { return a += b; } 21 | friend Point3 operator-(Point3 a, Point3 b) { return a -= b; } 22 | friend Point3 operator*(Point3 a, Point3 b) { return a *= b; } 23 | friend Point3 operator*(Point3 a, ld b) { return a *= b; } 24 | friend Point3 operator*(ld a, Point3 b) { return b *= a; } 25 | friend Point3 operator/(Point3 a, ld b) { return a /= b; } 26 | friend auto &operator>>(istream &is, Point3 &p) { 27 | return is >> p.x >> p.y >> p.z; 28 | } 29 | friend auto &operator<<(ostream &os, Point3 p) { 30 | return os << "(" << p.x << ", " << p.y << ", " << p.z << ")"; 31 | } 32 | }; 33 | struct Line3 { 34 | Point3 a, b; 35 | }; 36 | struct Plane { 37 | Point3 u, v, w; 38 | }; 39 | 40 | ld len(P3 p) { // 原点到当前点的距离计算 41 | return sqrt(p.x * p.x + p.y * p.y + p.z * p.z); 42 | } 43 | P3 crossEx(P3 a, P3 b) { // 叉乘 44 | P3 ans; 45 | ans.x = a.y * b.z - a.z * b.y; 46 | ans.y = a.z * b.x - a.x * b.z; 47 | ans.z = a.x * b.y - a.y * b.x; 48 | return ans; 49 | } 50 | ld cross(P3 a, P3 b) { 51 | return len(crossEx(a, b)); 52 | } 53 | ld dot(P3 a, P3 b) { // 点乘 54 | return a.x * b.x + a.y * b.y + a.z * b.z; 55 | } 56 | P3 getVec(Plane s) { // 获取平面法向量 57 | return crossEx(s.u - s.v, s.v - s.w); 58 | } 59 | ld dis(P3 a, P3 b) { // 三维欧几里得距离公式 60 | ld val = (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z); 61 | return sqrt(val); 62 | } 63 | P3 standardize(P3 vec) { // 将三维向量转换为单位向量 64 | return vec / len(vec); 65 | } 66 | } // namespace Geometry -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/24 - DSU++.cpp: -------------------------------------------------------------------------------- 1 | struct DSU { 2 | vector fa, p, e, f; 3 | 4 | DSU(int n) { 5 | fa.resize(n + 1); 6 | iota(fa.begin(), fa.end(), 0); 7 | p.resize(n + 1, 1); 8 | e.resize(n + 1); 9 | f.resize(n + 1); 10 | } 11 | int get(int x) { 12 | while (x != fa[x]) { 13 | x = fa[x] = fa[fa[x]]; 14 | } 15 | return x; 16 | } 17 | bool merge(int x, int y) { // 设x是y的祖先 18 | if (x == y) f[get(x)] = 1; 19 | x = get(x), y = get(y); 20 | e[x]++; 21 | if (x == y) return false; 22 | if (x < y) swap(x, y); // 将编号小的合并到大的上 23 | fa[y] = x; 24 | f[x] |= f[y], p[x] += p[y], e[x] += e[y]; 25 | return true; 26 | } 27 | bool same(int x, int y) { 28 | return get(x) == get(y); 29 | } 30 | bool F(int x) { // 判断连通块内是否存在自环 31 | return f[get(x)]; 32 | } 33 | int size(int x) { // 输出连通块中点的数量 34 | return p[get(x)]; 35 | } 36 | int E(int x) { // 输出连通块中边的数量 37 | return e[get(x)]; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/24 - DSU.cpp: -------------------------------------------------------------------------------- 1 | struct DSU { 2 | vector fa, p; 3 | 4 | DSU(int n) { 5 | fa.resize(n + 1); 6 | iota(fa.begin(), fa.end(), 0); 7 | p.resize(n + 1, 1); 8 | } 9 | int get(int x) { 10 | while (x != fa[x]) { 11 | x = fa[x] = fa[fa[x]]; 12 | } 13 | return x; 14 | } 15 | bool merge(int x, int y) { // 设x是y的祖先 16 | x = get(x), y = get(y); 17 | if (x == y) return false; 18 | if (x < y) swap(x, y); // 将编号小的合并到大的上 19 | fa[y] = x; 20 | p[x] += p[y]; 21 | return true; 22 | } 23 | bool same(int x, int y) { 24 | return get(x) == get(y); 25 | } 26 | int size(int x) { // 输出连通块中点的数量 27 | return p[get(x)]; 28 | } 29 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/25 - Fenwick 1D++.cpp: -------------------------------------------------------------------------------- 1 | struct BIT { 2 | int n; 3 | vector w; 4 | 5 | BIT() {} 6 | BIT(int n) { 7 | this->n = n; // 注意 n 不能 +1 8 | w.resize(n + 1); 9 | } 10 | void add(int x, int k) { 11 | for (; x <= n; x += x & -x) { 12 | w[x] += k; 13 | } 14 | } 15 | void add(int x, int y, int k) { 16 | add(x, k), add(y, -k); 17 | } 18 | int ask(int x) { 19 | int ans = 0; 20 | for (; x; x -= x & -x) { 21 | ans += w[x]; 22 | } 23 | return ans; 24 | } 25 | int ask(int x, int y) { 26 | return ask(y) - ask(x - 1); 27 | } 28 | int kth(int k) { // ex: 查找第k大的值 29 | int ans = 0; 30 | for (int i = __lg(n); i >= 0; i--) { 31 | int val = ans + (1 << i); 32 | if (val < n && w[val] < k) { 33 | k -= w[val]; 34 | ans = val; 35 | } 36 | } 37 | return ans + 1; 38 | } 39 | int get(auto val) { // ex: 获取逆序对数量 40 | this->n = val.size() - 1; // 注意 n 不能 +1 41 | w.resize(n + 1); 42 | 43 | vector> alls; 44 | for (int i = 1; i <= n; i++) { 45 | alls.emplace_back(val[i], i); 46 | } 47 | sort(alls.begin(), alls.end()); 48 | 49 | int ans = 0; 50 | for (auto [val, idx] : alls) { 51 | ans += ask(idx + 1, n); 52 | add(idx, 1); 53 | } 54 | return ans; 55 | } 56 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/25 - Fenwick 1D.cpp: -------------------------------------------------------------------------------- 1 | struct BIT { 2 | int n; 3 | vector w; 4 | 5 | BIT() {} 6 | BIT(int n) { 7 | this->n = n; // 这里必须写 n ,否则会RE 8 | w.resize(n + 1); 9 | } 10 | void add(int x, int k) { 11 | for (; x <= n; x += x & -x) { 12 | w[x] += k; 13 | } 14 | } 15 | void add(int x, int y, int k) { 16 | add(x, k), add(y, -k); 17 | } 18 | int ask(int x) { 19 | int ans = 0; 20 | for (; x; x -= x & -x) { 21 | ans += w[x]; 22 | } 23 | return ans; 24 | } 25 | int ask(int x, int y) { 26 | return ask(y) - ask(x - 1); 27 | } 28 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/25 - Fenwick 2D++.cpp: -------------------------------------------------------------------------------- 1 | struct BIT_2D { 2 | int n, m; 3 | vector> b1, b2, b3, b4; 4 | 5 | BIT_2D(int n, int m) : n(n), m(m) { 6 | b1.resize(n + 1, vector(m + 1)); 7 | b2.resize(n + 1, vector(m + 1)); 8 | b3.resize(n + 1, vector(m + 1)); 9 | b4.resize(n + 1, vector(m + 1)); 10 | } 11 | void add(auto &w, int x, int y, int k) { // 单点修改 12 | for (int i = x; i <= n; i += i & -i) { 13 | for (int j = y; j <= m; j += j & -j) { 14 | w[i][j] += k; 15 | } 16 | } 17 | } 18 | void add(int x, int y, int k) { // 多了一步计算 19 | add(b1, x, y, k); 20 | add(b2, x, y, k * (x - 1)); 21 | add(b3, x, y, k * (y - 1)); 22 | add(b4, x, y, k * (x - 1) * (y - 1)); 23 | } 24 | void add(int x, int y, int X, int Y, int k) { // 区块修改:二维差分 25 | X++, Y++; 26 | add(x, y, k), add(X, y, -k); 27 | add(X, Y, k), add(x, Y, -k); 28 | } 29 | int ask(auto &w, int x, int y) { // 单点查询 30 | int ans = 0; 31 | for (int i = x; i; i -= i & -i) { 32 | for (int j = y; j; j -= j & -j) { 33 | ans += w[i][j]; 34 | } 35 | } 36 | return ans; 37 | } 38 | int ask(int x, int y) { // 多了一步计算 39 | int ans = 0; 40 | ans += x * y * ask(b1, x, y); 41 | ans -= y * ask(b2, x, y); 42 | ans -= x * ask(b3, x, y); 43 | ans += ask(b4, x, y); 44 | return ans; 45 | } 46 | int ask(int x, int y, int X, int Y) { // 区块查询:二维前缀和 47 | x--, y--; 48 | return ask(X, Y) - ask(x, Y) - ask(X, y) + ask(x, y); 49 | } 50 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/25 - Fenwick 2D.cpp: -------------------------------------------------------------------------------- 1 | struct BIT_2D { 2 | int n, m; 3 | vector> w; 4 | 5 | BIT_2D(int n, int m) : n(n), m(m) { 6 | w.resize(n + 1, vector(m + 1)); 7 | } 8 | void add(int x, int y, int k) { 9 | for (int i = x; i <= n; i += i & -i) { 10 | for (int j = y; j <= m; j += j & -j) { 11 | w[i][j] += k; 12 | } 13 | } 14 | } 15 | void add(int x, int y, int X, int Y, int k) { // 区块修改:二维差分 16 | X++, Y++; 17 | add(x, y, k), add(X, y, -k); 18 | add(X, Y, k), add(x, Y, -k); 19 | } 20 | int ask(int x, int y) { // 单点查询 21 | int ans = 0; 22 | for (int i = x; i; i -= i & -i) { 23 | for (int j = y; j; j -= j & -j) { 24 | ans += w[i][j]; 25 | } 26 | } 27 | return ans; 28 | } 29 | int ask(int x, int y, int X, int Y) { // 区块查询:二维前缀和 30 | x--, y--; 31 | return ask(X, Y) - ask(x, Y) - ask(X, y) + ask(x, y); 32 | } 33 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/26 - Segt++.cpp: -------------------------------------------------------------------------------- 1 | template struct Segt_ { 2 | struct node { 3 | int l, r; 4 | T w, add, mul = 1; // 注意初始赋值 5 | }; 6 | vector w; 7 | vector t; 8 | 9 | Segt_(int n) { 10 | w.resize(n + 1); 11 | t.resize((n << 2) + 1); 12 | build(1, n); 13 | } 14 | Segt_(vector in) { 15 | int n = in.size() - 1; 16 | w.resize(n + 1); 17 | for (int i = 1; i <= n; i++) { 18 | w[i] = in[i]; 19 | } 20 | t.resize((n << 2) + 1); 21 | build(1, n); 22 | } 23 | void pushdown(node &p, T add, T mul) { // 在此更新下递函数 24 | p.w = p.w * mul + (p.r - p.l + 1) * add; 25 | p.add = p.add * mul + add; 26 | p.mul *= mul; 27 | } 28 | void pushup(node &p, node &l, node &r) { // 在此更新上传函数 29 | p.w = l.w + r.w; 30 | } 31 | #define GL (k << 1) 32 | #define GR (k << 1 | 1) 33 | void pushdown(int k) { // 不需要动 34 | pushdown(t[GL], t[k].add, t[k].mul); 35 | pushdown(t[GR], t[k].add, t[k].mul); 36 | t[k].add = 0, t[k].mul = 1; 37 | } 38 | void pushup(int k) { // 不需要动 39 | pushup(t[k], t[GL], t[GR]); 40 | } 41 | void build(int l, int r, int k = 1) { 42 | if (l == r) { 43 | t[k] = {l, r, w[l]}; 44 | return; 45 | } 46 | t[k] = {l, r}; 47 | int mid = (l + r) / 2; 48 | build(l, mid, GL); 49 | build(mid + 1, r, GR); 50 | pushup(k); 51 | } 52 | void modify(int l, int r, T val, int k = 1) { // 区间修改 53 | if (l <= t[k].l && t[k].r <= r) { 54 | t[k].w += (t[k].r - t[k].l + 1) * val; 55 | t[k].add += val; 56 | return; 57 | } 58 | pushdown(k); 59 | int mid = (t[k].l + t[k].r) / 2; 60 | if (l <= mid) modify(l, r, val, GL); 61 | if (mid < r) modify(l, r, val, GR); 62 | pushup(k); 63 | } 64 | void modify2(int l, int r, T val, int k = 1) { // 区间修改 65 | if (l <= t[k].l && t[k].r <= r) { 66 | t[k].w *= val; 67 | t[k].add *= val; 68 | t[k].mul *= val; 69 | return; 70 | } 71 | pushdown(k); 72 | int mid = (t[k].l + t[k].r) / 2; 73 | if (l <= mid) modify2(l, r, val, GL); 74 | if (mid < r) modify2(l, r, val, GR); 75 | pushup(k); 76 | } 77 | T ask(int l, int r, int k = 1) { // 区间询问,不合并 78 | if (l <= t[k].l && t[k].r <= r) { 79 | return t[k].w; 80 | } 81 | pushdown(k); 82 | int mid = (t[k].l + t[k].r) / 2; 83 | T ans = 0; 84 | if (l <= mid) ans += ask(l, r, GL); 85 | if (mid < r) ans += ask(l, r, GR); 86 | return ans; 87 | } 88 | void debug(int k = 1) { 89 | cout << "[" << t[k].l << ", " << t[k].r << "]: "; 90 | cout << "w = " << t[k].w << ", "; 91 | cout << "add = " << t[k].add << ", "; 92 | cout << "mul = " << t[k].mul << ", "; 93 | cout << endl; 94 | if (t[k].l == t[k].r) return; 95 | debug(GL), debug(GR); 96 | } 97 | #undef GL 98 | #undef GR 99 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/27 - 坐标压缩.cpp: -------------------------------------------------------------------------------- 1 | // 如果是前缀和离散化操作,务必记得要将每一个数字的后一个数字也传入计算。 2 | template struct Compress_ { 3 | int n, shift = 0; // shift 用于标记下标偏移量 4 | vector alls; 5 | 6 | Compress_() {} 7 | Compress_(auto in) : alls(in) { 8 | init(); 9 | } 10 | void add(T x) { 11 | alls.emplace_back(x); 12 | } 13 | template void add(T x, Args... args) { 14 | add(x), add(args...); 15 | } 16 | void init() { 17 | alls.emplace_back(numeric_limits::max()); 18 | sort(alls.begin(), alls.end()); 19 | alls.erase(unique(alls.begin(), alls.end()), alls.end()); 20 | this->n = alls.size(); 21 | } 22 | int size() { 23 | return n; 24 | } 25 | int operator[](T x) { // 返回 x 元素的新下标 26 | return upper_bound(alls.begin(), alls.end(), x) - alls.begin() + shift; 27 | } 28 | T Get(int x) { // 根据新下标返回原来元素 29 | assert(x - shift < n); 30 | return x - shift < n ? alls[x - shift] : -1; 31 | } 32 | bool count(T x) { // 查找元素 x 是否存在 33 | return binary_search(alls.begin(), alls.end(), x); 34 | } 35 | friend auto &operator<< (ostream &o, const auto &j) { 36 | cout << "{"; 37 | for (auto it : j.alls) { 38 | o << it << " "; 39 | } 40 | return o << "}"; 41 | } 42 | }; 43 | using Compress = Compress_; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/29 - 分数四则运算.cpp: -------------------------------------------------------------------------------- 1 | template struct Frac { 2 | T x, y; 3 | Frac() : Frac(0, 1) {} 4 | Frac(T x_) : Frac(x_, 1) {} 5 | Frac(T x_, T y_) : x(x_), y(y_) { 6 | if (y < 0) { 7 | y = -y; 8 | x = -x; 9 | } 10 | } 11 | 12 | constexpr double val() const { 13 | return 1. * x / y; 14 | } 15 | constexpr Frac norm() const { // 调整符号、转化为最简形式 16 | T p = gcd(x, y); 17 | return {x / p, y / p}; 18 | } 19 | friend constexpr auto &operator<<(ostream &o, const Frac &j) { 20 | T p = gcd(j.x, j.y); 21 | if (j.y == p) { 22 | return o << j.x / p; 23 | } else { 24 | return o << j.x / p << "/" << j.y / p; 25 | } 26 | } 27 | constexpr Frac &operator/=(const Frac &i) { 28 | x *= i.y; 29 | y *= i.x; 30 | if (y < 0) { 31 | x = -x; 32 | y = -y; 33 | } 34 | return *this; 35 | } 36 | constexpr Frac &operator+=(const Frac &i) { 37 | x = x * i.y + y * i.x; 38 | y *= i.y; 39 | return *this; 40 | } 41 | constexpr Frac &operator-=(const Frac &i) { 42 | x = x * i.y - y * i.x; 43 | y *= i.y; 44 | return *this; 45 | } 46 | constexpr Frac &operator*=(const Frac &i) { 47 | x *= i.x; 48 | y *= i.y; 49 | return *this; 50 | } 51 | friend constexpr Frac operator+(const Frac i, const Frac j) { return i += j; } 52 | friend constexpr Frac operator-(const Frac i, const Frac j) { return i -= j; } 53 | friend constexpr Frac operator*(const Frac i, const Frac j) { return i *= j; } 54 | friend constexpr Frac operator/(const Frac i, const Frac j) { return i /= j; } 55 | friend constexpr Frac operator-(const Frac i) { return Frac(-i.x, i.y); } 56 | friend constexpr bool operator<(const Frac i, const Frac j) { return i.x * j.y < i.y * j.x; } 57 | friend constexpr bool operator>(const Frac i, const Frac j) { return i.x * j.y > i.y * j.x; } 58 | friend constexpr bool operator==(const Frac i, const Frac j) { return i.x * j.y == i.y * j.x; } 59 | friend constexpr bool operator!=(const Frac i, const Frac j) { return i.x * j.y != i.y * j.x; } 60 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/30 - ST 表.cpp: -------------------------------------------------------------------------------- 1 | struct ST { 2 | const int n, k; 3 | vector in1, in2; 4 | vector> Max, Min; 5 | ST(int n) : n(n), in1(n + 1), in2(n + 1), k(__lg(n)) { 6 | Max.resize(k + 1, vector(n + 1)); 7 | Min.resize(k + 1, vector(n + 1)); 8 | } 9 | void init() { 10 | for (int i = 1; i <= n; i++) { 11 | Max[0][i] = in1[i]; 12 | Min[0][i] = in2[i]; 13 | } 14 | for (int i = 0, t = 1; i < k; i++, t <<= 1) { 15 | const int T = n - (t << 1) + 1; 16 | for (int j = 1; j <= T; j++) { 17 | Max[i + 1][j] = max(Max[i][j], Max[i][j + t]); 18 | Min[i + 1][j] = min(Min[i][j], Min[i][j + t]); 19 | } 20 | } 21 | } 22 | int getMax(int l, int r) { 23 | if (l > r) { 24 | swap(l, r); 25 | } 26 | int k = __lg(r - l + 1); 27 | return max(Max[k][l], Max[k][r - (1 << k) + 1]); 28 | } 29 | int getMin(int l, int r) { 30 | if (l > r) { 31 | swap(l, r); 32 | } 33 | int k = __lg(r - l + 1); 34 | return min(Min[k][l], Min[k][r - (1 << k) + 1]); 35 | } 36 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/31 - 线性基(高斯消元法).cpp: -------------------------------------------------------------------------------- 1 | struct LB { // Linear Basis 2 | using i64 = long long; 3 | const int BASE = 63; 4 | vector d, p; 5 | int cnt, flag; 6 | 7 | LB() { 8 | d.resize(BASE + 1); 9 | p.resize(BASE + 1); 10 | cnt = flag = 0; 11 | } 12 | bool insert(i64 val) { 13 | for (int i = BASE - 1; i >= 0; i--) { 14 | if (val & (1ll << i)) { 15 | if (!d[i]) { 16 | d[i] = val; 17 | return true; 18 | } 19 | val ^= d[i]; 20 | } 21 | } 22 | flag = 1; //可以异或出0 23 | return false; 24 | } 25 | bool check(i64 val) { // 判断 val 是否能被异或得到 26 | for (int i = BASE - 1; i >= 0; i--) { 27 | if (val & (1ll << i)) { 28 | if (!d[i]) { 29 | return false; 30 | } 31 | val ^= d[i]; 32 | } 33 | } 34 | return true; 35 | } 36 | i64 ask_max() { 37 | i64 res = 0; 38 | for (int i = BASE - 1; i >= 0; i--) { 39 | if ((res ^ d[i]) > res) res ^= d[i]; 40 | } 41 | return res; 42 | } 43 | i64 ask_min() { 44 | if (flag) return 0; // 特判 0 45 | for (int i = 0; i <= BASE - 1; i++) { 46 | if (d[i]) return d[i]; 47 | } 48 | } 49 | void rebuild() { // 第k小值独立预处理 50 | for (int i = BASE - 1; i >= 0; i--) { 51 | for (int j = i - 1; j >= 0; j--) { 52 | if (d[i] & (1ll << j)) d[i] ^= d[j]; 53 | } 54 | } 55 | for (int i = 0; i <= BASE - 1; i++) { 56 | if (d[i]) p[cnt++] = d[i]; 57 | } 58 | } 59 | i64 kthquery(i64 k) { // 查询能被异或得到的第 k 小值, 如不存在则返回 -1 60 | if (flag) k--; // 特判 0, 如果不需要 0, 直接删去 61 | if (!k) return 0; 62 | i64 res = 0; 63 | if (k >= (1ll << cnt)) return -1; 64 | for (int i = BASE - 1; i >= 0; i--) { 65 | if (k & (1LL << i)) res ^= p[i]; 66 | } 67 | return res; 68 | } 69 | void Merge(const LB &b) { // 合并两个线性基 70 | for (int i = BASE - 1; i >= 0; i--) { 71 | if (b.d[i]) { 72 | insert(b.d[i]); 73 | } 74 | } 75 | } 76 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/32 - 取模类.cpp: -------------------------------------------------------------------------------- 1 | using i64 = long long; 2 | 3 | template constexpr T mypow(T n, i64 k) { 4 | T r = 1; 5 | for (; k; k /= 2, n *= n) { 6 | if (k % 2) { 7 | r *= n; 8 | } 9 | } 10 | return r; 11 | } 12 | 13 | template struct Zmod { 14 | int x; 15 | Zmod(int x = 0) : x(norm(x % MOD)) {} 16 | Zmod(i64 x) : x(norm(x % MOD)) {} 17 | 18 | constexpr int norm(int x) const { 19 | if (x < 0) { 20 | x += MOD; 21 | } 22 | if (x >= MOD) { 23 | x -= MOD; 24 | } 25 | return x; 26 | } 27 | constexpr int val() const { 28 | return x; 29 | } 30 | constexpr Zmod operator-() const { 31 | Zmod val = norm(MOD - x); 32 | return val; 33 | } 34 | constexpr Zmod inv() const { 35 | assert(x != 0); 36 | return mypow(*this, MOD - 2); 37 | } 38 | friend constexpr auto &operator>>(istream &in, Zmod &j) { 39 | int v; 40 | in >> v; 41 | j = Zmod(v); 42 | return in; 43 | } 44 | friend constexpr auto &operator<<(ostream &o, const Zmod &j) { 45 | return o << j.val(); 46 | } 47 | constexpr Zmod &operator++() { 48 | x = norm(x + 1); 49 | return *this; 50 | } 51 | constexpr Zmod &operator--() { 52 | x = norm(x - 1); 53 | return *this; 54 | } 55 | constexpr Zmod &operator+=(const Zmod &i) { 56 | x = norm(x + i.x); 57 | return *this; 58 | } 59 | constexpr Zmod &operator-=(const Zmod &i) { 60 | x = norm(x - i.x); 61 | return *this; 62 | } 63 | constexpr Zmod &operator*=(const Zmod &i) { 64 | x = i64(x) * i.x % MOD; 65 | return *this; 66 | } 67 | constexpr Zmod &operator/=(const Zmod &i) { 68 | return *this *= i.inv(); 69 | } 70 | constexpr Zmod &operator%=(const int &i) { 71 | return x %= i, *this; 72 | } 73 | friend constexpr Zmod operator+(const Zmod i, const Zmod j) { 74 | return Zmod(i) += j; 75 | } 76 | friend constexpr Zmod operator-(const Zmod i, const Zmod j) { 77 | return Zmod(i) -= j; 78 | } 79 | friend constexpr Zmod operator*(const Zmod i, const Zmod j) { 80 | return Zmod(i) *= j; 81 | } 82 | friend constexpr Zmod operator/(const Zmod i, const Zmod j) { 83 | return Zmod(i) /= j; 84 | } 85 | friend constexpr Zmod operator%(const Zmod i, const int j) { 86 | return Zmod(i) %= j; 87 | } 88 | friend constexpr bool operator==(const Zmod i, const Zmod j) { 89 | return i.val() == j.val(); 90 | } 91 | friend constexpr bool operator!=(const Zmod i, const Zmod j) { 92 | return i.val() != j.val(); 93 | } 94 | friend constexpr bool operator<(const Zmod i, const Zmod j) { 95 | return i.val() < j.val(); 96 | } 97 | friend constexpr bool operator>(const Zmod i, const Zmod j) { 98 | return i.val() > j.val(); 99 | } 100 | }; 101 | 102 | constexpr int MOD[] = {998244353, 1000000007}; 103 | using Z = Zmod; 104 | 105 | Z power(int n) { 106 | return mypow(Z(2), n); 107 | } -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/34 - KMP.cpp: -------------------------------------------------------------------------------- 1 | auto KMP = [&](string s, string t) -> void { 2 | int n = s.size(), m = t.size(); 3 | vector nxt(m + 1); 4 | s = '-' + s; 5 | t = '-' + t; 6 | for (int i = 2, j = 0; i <= m; i++) { 7 | while (j && t[i] != t[j + 1]) j = nxt[j]; 8 | if (t[i] == t[j + 1]) j++; 9 | nxt[i] = j; 10 | } 11 | for (int i = 1, j = 0; i <= n; i++) { 12 | while (j && s[i] != t[j + 1]) j = nxt[j]; 13 | if (s[i] == t[j + 1]) j++; 14 | if (j == m) { 15 | cout << i - m + 1 << "\n"; // t 在 s 中出现的位置 16 | j = nxt[j]; 17 | } 18 | } 19 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/35 - 字符串双哈希++.cpp: -------------------------------------------------------------------------------- 1 | mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count()); 2 | int r(int a, int b) { return rnd() % (b - a + 1) + a; } 3 | int add(int a, int b, int mod) { 4 | a += b; 5 | return a >= mod ? a - mod : a; 6 | } 7 | int sub(int a, int b, int mod) { 8 | a -= b; 9 | return a < 0 ? a + mod : a; 10 | } 11 | int mul(int a, int b, int mod) { 12 | int r = a * b - mod * (int)(1.L / mod * a * b); 13 | return r - mod * (r >= mod) + mod * (r < 0); 14 | } 15 | int mypow(int a, int b, int mod) { 16 | int res(1); 17 | for (; b; b >>= 1, a = mul(a, a, mod)) { 18 | if (b & 1) res = mul(res, a, mod); 19 | } 20 | return res; 21 | } 22 | bool Miller_rabin(int n) { // 素数测试 23 | if (n <= 1) return false; 24 | vector base = {2, 3, 5, 7, 11, 13, 17, 19, 23}; 25 | for (int p : base) { 26 | if (n == p) return true; 27 | if (n % p == 0) return false; 28 | } 29 | int m = (n - 1) >> __builtin_ctz(n - 1); 30 | for (int p : base) { 31 | int t = m, a = mypow(p, m, n); 32 | while (t != n - 1 && a != 1 && a != n - 1) { 33 | a = mul(a, a, n); 34 | t *= 2; 35 | } 36 | if (a != n - 1 && t % 2 == 0) return false; 37 | } 38 | return true; 39 | } 40 | int findPrime(int n) { 41 | while (!Miller_rabin(n)) { 42 | n++; 43 | } 44 | return n; 45 | } 46 | 47 | using pii = pair; 48 | 49 | struct String { 50 | int MOD, BASE; 51 | vector val; 52 | 53 | String() {} 54 | String(int MOD, int BASE, int N) { init(MOD, BASE, N); } 55 | void init(int MOD, int BASE, int N) { 56 | this->MOD = MOD; 57 | this->BASE = BASE; 58 | val.resize(N + 1); 59 | val[0] = 1; 60 | for (int i = 1; i <= N; i++) { 61 | val[i] = (val[i - 1] * BASE) % MOD; 62 | } 63 | } 64 | int get(const string &s) { 65 | int ans = 0; 66 | for (auto i : s) { 67 | ans = (ans * BASE + i - 'a') % MOD; 68 | } 69 | return ans; 70 | } 71 | int operator[](const string &s) { 72 | return get(s); 73 | } 74 | int modify(int x, string &s, int idx, char now) { 75 | int n = s.size() - 1; 76 | return (x + val[n - idx] * (now - s[idx]) % MOD + MOD) % MOD; 77 | } 78 | }; 79 | 80 | struct StringHash { 81 | String chk1, chk2; 82 | StringHash(int n = 1 << 21) { 83 | int mod1 = findPrime(r(1E8, 5E8)), base1 = findPrime(r(2, 100)); 84 | int mod2 = findPrime(r(mod1, 1E9)), base2 = findPrime(r(base1, 200)); 85 | chk1.init(mod1, base1, n); 86 | chk2.init(mod2, base2, n); 87 | } 88 | pii operator[](const string &s) { 89 | return {chk1[s], chk2[s]}; 90 | } 91 | pii modify(pii x, string &s, int idx, char now) { 92 | return {chk1.modify(x.first, s, idx, now), chk2.modify(x.second, s, idx, now)}; 93 | } 94 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/35 - 字符串双哈希.cpp: -------------------------------------------------------------------------------- 1 | using pii = pair; 2 | struct String { 3 | int MOD, BASE; 4 | vector val; 5 | String() {} 6 | void init(int MOD, int BASE, int N) { 7 | this->MOD = MOD; 8 | this->BASE = BASE; 9 | val.resize(N + 1); 10 | val[0] = 1; 11 | for (int i = 1; i <= N; i++) { 12 | val[i] = (val[i - 1] * BASE) % MOD; 13 | } 14 | } 15 | int get(const string &s) { 16 | int ans = 0; 17 | for (auto i : s) { 18 | ans = (ans * BASE + i) % MOD; 19 | } 20 | return ans; 21 | } 22 | int operator[](const string &s) { 23 | return get(s); 24 | } 25 | int modify(int x, string &s, int idx, char now) { 26 | int n = s.size() - 1; 27 | return (x + val[n - idx] * (now - s[idx]) % MOD + MOD) % MOD; 28 | } 29 | }; 30 | struct StringHash { 31 | String chk1, chk2; 32 | StringHash(int n = 1 << 21) { 33 | int mod1 = 1E9 + 7, base1 = 127; 34 | int mod2 = 1E9 + 9, base2 = 131; 35 | chk1.init(mod1, base1, n); 36 | chk2.init(mod2, base2, n); 37 | } 38 | pii operator[](const string &s) { 39 | return {chk1[s], chk2[s]}; 40 | } 41 | pii modify(pii x, string &s, int idx, char now) { 42 | return {chk1.modify(x.first, s, idx, now), chk2.modify(x.second, s, idx, now)}; 43 | } 44 | }; -------------------------------------------------------------------------------- /01 - 常用在线模板汇总/04 - 串与数据结构/35 - 字符串哈希.cpp: -------------------------------------------------------------------------------- 1 | struct String { 2 | int MOD, BASE; 3 | vector val; 4 | 5 | String(int MOD, int BASE, int N) { 6 | init(MOD, BASE, N); 7 | } 8 | void init(int MOD, int BASE, int N) { 9 | this->MOD = MOD; 10 | this->BASE = BASE; 11 | val.resize(N + 1); 12 | val[0] = 1; 13 | for (int i = 1; i <= N; i++) { 14 | val[i] = (val[i - 1] * BASE) % MOD; 15 | } 16 | } 17 | int get(const string &s) { 18 | int ans = 0; 19 | for (auto i : s) { 20 | ans = (ans * BASE + i) % MOD; 21 | } 22 | return ans; 23 | } 24 | int operator[](const int &s) { 25 | return val[s]; 26 | } 27 | int operator[](const string &s) { 28 | return get(s); 29 | } 30 | int modify(int x, string &s, int idx, char now) { 31 | int n = s.size() - 1; 32 | return (x + val[n - idx] * (now - s[idx]) % MOD + MOD) % MOD; 33 | } 34 | }; 35 | 36 | const int N = 1E5; 37 | const int mod = 1E9 + 7; 38 | const int base = 127; -------------------------------------------------------------------------------- /03 - jiangly模板收集/01 - 杂类/01 - int128 库函数自定义.cpp: -------------------------------------------------------------------------------- 1 | /** int128 库函数自定义 2 | * 2024-08-14: https://ac.nowcoder.com/acm/contest/view-submission?submissionId=70979004&returnHomeType=1&uid=329687984 3 | * 2024-09-17: https://qoj.ac/submission/571481 4 | **/ 5 | using i128 = __int128; 6 | 7 | std::ostream &operator<<(std::ostream &os, i128 n) { 8 | if (n == 0) { 9 | return os << 0; 10 | } 11 | std::string s; 12 | while (n > 0) { 13 | s += char('0' + n % 10); 14 | n /= 10; 15 | } 16 | std::reverse(s.begin(), s.end()); 17 | return os << s; 18 | } 19 | 20 | i128 toi128(const std::string &s) { 21 | i128 n = 0; 22 | for (auto c : s) { 23 | n = n * 10 + (c - '0'); 24 | } 25 | return n; 26 | } 27 | 28 | i128 sqrti128(i128 n) { 29 | i128 lo = 0, hi = 1E16; 30 | while (lo < hi) { 31 | i128 x = (lo + hi + 1) / 2; 32 | if (x * x <= n) { 33 | lo = x; 34 | } else { 35 | hi = x - 1; 36 | } 37 | } 38 | return lo; 39 | } 40 | 41 | i128 gcd(i128 a, i128 b) { 42 | while (b) { 43 | a %= b; 44 | std::swap(a, b); 45 | } 46 | return a; 47 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/01 - 杂类/02 - 常用库函数重载.cpp: -------------------------------------------------------------------------------- 1 | using i64 = long long; 2 | using i128 = __int128; 3 | 4 | /** 上取整下取整 5 | * 2023-10-15: https://codeforces.com/contest/293/submission/228297248 6 | **/ 7 | i64 ceilDiv(i64 n, i64 m) { 8 | if (n >= 0) { 9 | return (n + m - 1) / m; 10 | } else { 11 | return n / m; 12 | } 13 | } 14 | 15 | i64 floorDiv(i64 n, i64 m) { 16 | if (n >= 0) { 17 | return n / m; 18 | } else { 19 | return (n - m + 1) / m; 20 | } 21 | } 22 | 23 | /** 最大值赋值 24 | * 2023-09-30: https://codeforces.com/contest/1874/submission/226069129 25 | **/ 26 | template 27 | void chmax(T &a, T b) { 28 | if (a < b) { 29 | a = b; 30 | } 31 | } 32 | 33 | /** 最大公约数 34 | * -: - 35 | **/ 36 | i128 gcd(i128 a, i128 b) { 37 | return b ? gcd(b, a % b) : a; 38 | } 39 | 40 | /** 精确开平方 41 | * 2024-03-02: https://qoj.ac/submission/343317 42 | **/ 43 | i64 sqrt(i64 n) { 44 | i64 s = std::sqrt(n); 45 | while (s * s > n) { 46 | s--; 47 | } 48 | while ((s + 1) * (s + 1) <= n) { 49 | s++; 50 | } 51 | return s; 52 | } 53 | 54 | /** 精确开平方 55 | * 2023-09-19: https://qoj.ac/submission/183430 56 | **/ 57 | i64 get(i64 n) { 58 | i64 u = std::sqrt(2.0L * n); 59 | while (u * (u + 1) / 2 < n) { 60 | u++; 61 | } 62 | while (u * (u - 1) / 2 + 1 > n) { 63 | u--; 64 | } 65 | return u; 66 | } 67 | 68 | /** 求 Log 69 | * 2024-07-23: https://codeforces.com/contest/1995/submission/272110180 70 | **/ 71 | int logi(int a, int b) { 72 | int t = 0; 73 | i64 v = 1; 74 | while (v < b) { 75 | v *= a; 76 | t++; 77 | } 78 | return t; 79 | } 80 | 81 | int llog(int a, int b) { 82 | if (a <= b) { 83 | int l = logi(a, b); 84 | return (l == 0 ? 0 : std::__lg(2 * l - 1)); 85 | } 86 | int l = logi(b, a + 1) - 1; 87 | assert(l > 0); 88 | return -std::__lg(l); 89 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/01 - 杂类/03 - 字符调整.cpp: -------------------------------------------------------------------------------- 1 | /** 大小写转换、获取字母序 2 | * 2024-03-16: https://qoj.ac/submission/355156 3 | **/ 4 | void rev(std::string &s) { 5 | int l = s.size(); 6 | for (int i = 1; i < l; i += 2) { 7 | if (std::isupper(s[i])) { 8 | s[i] = std::tolower(s[i]); 9 | } else { 10 | s[i] = std::toupper(s[i]); 11 | } 12 | } 13 | } 14 | 15 | int get(char c) { 16 | int x; 17 | if (std::islower(c)) { 18 | x = c - 'a'; 19 | } else { 20 | x = 26 + c - 'A'; 21 | } 22 | return x; 23 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/01 - 杂类/04A - 二分算法(整数域).cpp: -------------------------------------------------------------------------------- 1 | /** 二分算法(整数域): 前驱 2 | * 2023-09-18: https://qoj.ac/submission/182628 3 | **/ 4 | int lo = 1, hi = 1E9; 5 | while (lo < hi) { 6 | int m = (lo + hi + 1) / 2; 7 | if (check(m)) { 8 | lo = m; 9 | } else { 10 | hi = m - 1; 11 | } 12 | } 13 | std::cout << lo << "\n"; 14 | 15 | /** 二分算法(整数域):后继 16 | * 2023-09-18: https://qoj.ac/submission/182752 17 | **/ 18 | int lo = 1, hi = n; 19 | while (lo < hi) { 20 | int m = (lo + hi) / 2; 21 | if (check(m)) { 22 | hi = m; 23 | } else { 24 | lo = m + 1; 25 | } 26 | } 27 | std::cout << lo << "\n"; -------------------------------------------------------------------------------- /03 - jiangly模板收集/01 - 杂类/04B - 二分算法(实数域).cpp: -------------------------------------------------------------------------------- 1 | /** 二分算法(实数域) 2 | * 2023-10-21: https://qoj.ac/submission/222042 3 | **/ 4 | auto check = [&](double t) { 5 | // write 6 | }; 7 | 8 | double lo = 0; 9 | double hi = 1E12; 10 | while (hi - lo > std::max(1.0, lo) * eps) { 11 | double x = (lo + hi) / 2; 12 | if (check(x)) { 13 | hi = x; 14 | } else { 15 | lo = x; 16 | } 17 | } 18 | 19 | std::cout << lo << "\n"; 20 | 21 | /** 二分算法(实数域) 22 | * 2023-09-15: https://qoj.ac/submission/179994 23 | **/ 24 | using i64 = long long; 25 | using real = long double; 26 | 27 | constexpr real eps = 1E-7; 28 | 29 | auto get = [&](const auto &f) { 30 | real lo = -1E4, hi = 1E4; 31 | while (hi - lo > 3 * eps) { 32 | real x1 = (lo + hi - eps) / 2; 33 | real x2 = (lo + hi + eps) / 2; 34 | if (f(x1) > f(x2)) { 35 | lo = x1; 36 | } else { 37 | hi = x2; 38 | } 39 | } 40 | return f((lo + hi) / 2); 41 | }; 42 | 43 | std::cout << get([&](real px) { 44 | return get([&](real py) { 45 | // write 46 | }); 47 | }) << "\n"; -------------------------------------------------------------------------------- /03 - jiangly模板收集/01 - 杂类/05 - 快读.cpp: -------------------------------------------------------------------------------- 1 | /** 快读 2 | * 2023-08-11: https://ac.nowcoder.com/acm/contest/view-submission?submissionId=63381475&returnHomeType=1&uid=815516497 3 | * * 感谢菜菜园子群友提供 4 | **/ 5 | struct IO { 6 | char a[1 << 25], b[1 << 25], *s, *t; 7 | IO() : s(a), t(b) { 8 | a[std::fread(a, 1, sizeof a, stdin)] = 0; 9 | } 10 | ~IO() { 11 | std::fwrite(b, 1, t - b, stdout); 12 | } 13 | IO &operator>>(std::uint64_t &x); 14 | IO &operator>>(std::int64_t &x); 15 | IO &operator>>(std::int32_t &x); 16 | IO &operator>>(std::uint32_t &x) { 17 | x = 0; 18 | 19 | while (*s < '0' || *s > '9') 20 | ++s; 21 | 22 | while (*s >= '0' && *s <= '9') 23 | x = x * 10 + *s++ - '0'; 24 | 25 | return *this; 26 | } 27 | IO &operator<<(const char *tmp) { 28 | return std::fwrite(tmp, 1, std::strlen(tmp), stdout), *this; 29 | } 30 | IO &operator<<(char x) { 31 | return *t++ = x, *this; 32 | } 33 | IO &operator<<(std::int32_t x); 34 | IO &operator<<(std::uint64_t x); 35 | IO &operator<<(std::int64_t x); 36 | IO &operator<<(std::uint32_t x) { 37 | static char c[16], *i; 38 | i = c; 39 | 40 | if (x == 0) { 41 | *t++ = '0'; 42 | } else { 43 | while (x != 0) { 44 | std::uint32_t y = x / 10; 45 | *i++ = x - y * 10 + '0', x = y; 46 | } 47 | 48 | while (i != c) 49 | *t++ = *--i; 50 | } 51 | 52 | return *this; 53 | } 54 | } io; -------------------------------------------------------------------------------- /03 - jiangly模板收集/02 - 图与网络/01 - 强连通分量缩点(SCC).cpp: -------------------------------------------------------------------------------- 1 | /** 强连通分量缩点(SCC) 2 | * 2023-06-18: https://codeforces.com/contest/1835/submission/210147209 3 | **/ 4 | struct SCC { 5 | int n; 6 | std::vector> adj; 7 | std::vector stk; 8 | std::vector dfn, low, bel; 9 | int cur, cnt; 10 | 11 | SCC() {} 12 | SCC(int n) { 13 | init(n); 14 | } 15 | 16 | void init(int n) { 17 | this->n = n; 18 | adj.assign(n, {}); 19 | dfn.assign(n, -1); 20 | low.resize(n); 21 | bel.assign(n, -1); 22 | stk.clear(); 23 | cur = cnt = 0; 24 | } 25 | 26 | void addEdge(int u, int v) { 27 | adj[u].push_back(v); 28 | } 29 | 30 | void dfs(int x) { 31 | dfn[x] = low[x] = cur++; 32 | stk.push_back(x); 33 | 34 | for (auto y : adj[x]) { 35 | if (dfn[y] == -1) { 36 | dfs(y); 37 | low[x] = std::min(low[x], low[y]); 38 | } else if (bel[y] == -1) { 39 | low[x] = std::min(low[x], dfn[y]); 40 | } 41 | } 42 | 43 | if (dfn[x] == low[x]) { 44 | int y; 45 | do { 46 | y = stk.back(); 47 | bel[y] = cnt; 48 | stk.pop_back(); 49 | } while (y != x); 50 | cnt++; 51 | } 52 | } 53 | 54 | std::vector work() { 55 | for (int i = 0; i < n; i++) { 56 | if (dfn[i] == -1) { 57 | dfs(i); 58 | } 59 | } 60 | return bel; 61 | } 62 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/02 - 图与网络/02 - 割边与割边缩点(EBCC).cpp: -------------------------------------------------------------------------------- 1 | /** 割边与割边缩点(EBCC) 2 | * 2023-05-11: https://codeforces.com/contest/118/submission/205426518 3 | **/ 4 | std::set> E; 5 | 6 | struct EBCC { 7 | int n; 8 | std::vector> adj; 9 | std::vector stk; 10 | std::vector dfn, low, bel; 11 | int cur, cnt; 12 | 13 | EBCC() {} 14 | EBCC(int n) { 15 | init(n); 16 | } 17 | 18 | void init(int n) { 19 | this->n = n; 20 | adj.assign(n, {}); 21 | dfn.assign(n, -1); 22 | low.resize(n); 23 | bel.assign(n, -1); 24 | stk.clear(); 25 | cur = cnt = 0; 26 | } 27 | 28 | void addEdge(int u, int v) { 29 | adj[u].push_back(v); 30 | adj[v].push_back(u); 31 | } 32 | 33 | void dfs(int x, int p) { 34 | dfn[x] = low[x] = cur++; 35 | stk.push_back(x); 36 | 37 | for (auto y : adj[x]) { 38 | if (y == p) { 39 | continue; 40 | } 41 | if (dfn[y] == -1) { 42 | E.emplace(x, y); 43 | dfs(y, x); 44 | low[x] = std::min(low[x], low[y]); 45 | } else if (bel[y] == -1 && dfn[y] < dfn[x]) { 46 | E.emplace(x, y); 47 | low[x] = std::min(low[x], dfn[y]); 48 | } 49 | } 50 | 51 | if (dfn[x] == low[x]) { 52 | int y; 53 | do { 54 | y = stk.back(); 55 | bel[y] = cnt; 56 | stk.pop_back(); 57 | } while (y != x); 58 | cnt++; 59 | } 60 | } 61 | 62 | std::vector work() { 63 | dfs(0, -1); 64 | return bel; 65 | } 66 | 67 | struct Graph { 68 | int n; 69 | std::vector> edges; 70 | std::vector siz; 71 | std::vector cnte; 72 | }; 73 | Graph compress() { 74 | Graph g; 75 | g.n = cnt; 76 | g.siz.resize(cnt); 77 | g.cnte.resize(cnt); 78 | for (int i = 0; i < n; i++) { 79 | g.siz[bel[i]]++; 80 | for (auto j : adj[i]) { 81 | if (bel[i] < bel[j]) { 82 | g.edges.emplace_back(bel[i], bel[j]); 83 | } else if (i < j) { 84 | g.cnte[bel[i]]++; 85 | } 86 | } 87 | } 88 | return g; 89 | } 90 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/02 - 图与网络/05 - TwoSat(2-Sat).cpp: -------------------------------------------------------------------------------- 1 | /** TwoSat(2-Sat) 2 | * 2023-09-29: https://atcoder.jp/contests/arc161/submissions/46031530 3 | **/ 4 | struct TwoSat { 5 | int n; 6 | std::vector> e; 7 | std::vector ans; 8 | TwoSat(int n) : n(n), e(2 * n), ans(n) {} 9 | void addClause(int u, bool f, int v, bool g) { 10 | e[2 * u + !f].push_back(2 * v + g); 11 | e[2 * v + !g].push_back(2 * u + f); 12 | } 13 | bool satisfiable() { 14 | std::vector id(2 * n, -1), dfn(2 * n, -1), low(2 * n, -1); 15 | std::vector stk; 16 | int now = 0, cnt = 0; 17 | std::function tarjan = [&](int u) { 18 | stk.push_back(u); 19 | dfn[u] = low[u] = now++; 20 | for (auto v : e[u]) { 21 | if (dfn[v] == -1) { 22 | tarjan(v); 23 | low[u] = std::min(low[u], low[v]); 24 | } else if (id[v] == -1) { 25 | low[u] = std::min(low[u], dfn[v]); 26 | } 27 | } 28 | if (dfn[u] == low[u]) { 29 | int v; 30 | do { 31 | v = stk.back(); 32 | stk.pop_back(); 33 | id[v] = cnt; 34 | } while (v != u); 35 | ++cnt; 36 | } 37 | }; 38 | for (int i = 0; i < 2 * n; ++i) if (dfn[i] == -1) tarjan(i); 39 | for (int i = 0; i < n; ++i) { 40 | if (id[2 * i] == id[2 * i + 1]) return false; 41 | ans[i] = id[2 * i] > id[2 * i + 1]; 42 | } 43 | return true; 44 | } 45 | std::vector answer() { return ans; } 46 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/02 - 图与网络/06A - 最大流(Flow 旧版其一,整数应用).cpp: -------------------------------------------------------------------------------- 1 | /** 最大流(Flow 旧版其一,整数应用) 2 | * 2022-09-03: https://codeforces.com/contest/1717/submission/170688062 3 | **/ 4 | template 5 | struct Flow { 6 | const int n; 7 | struct Edge { 8 | int to; 9 | T cap; 10 | Edge(int to, T cap) : to(to), cap(cap) {} 11 | }; 12 | std::vector e; 13 | std::vector> g; 14 | std::vector cur, h; 15 | Flow(int n) : n(n), g(n) {} 16 | 17 | bool bfs(int s, int t) { 18 | h.assign(n, -1); 19 | std::queue que; 20 | h[s] = 0; 21 | que.push(s); 22 | while (!que.empty()) { 23 | const int u = que.front(); 24 | que.pop(); 25 | for (int i : g[u]) { 26 | auto [v, c] = e[i]; 27 | if (c > 0 && h[v] == -1) { 28 | h[v] = h[u] + 1; 29 | if (v == t) { 30 | return true; 31 | } 32 | que.push(v); 33 | } 34 | } 35 | } 36 | return false; 37 | } 38 | 39 | T dfs(int u, int t, T f) { 40 | if (u == t) { 41 | return f; 42 | } 43 | auto r = f; 44 | for (int &i = cur[u]; i < int(g[u].size()); ++i) { 45 | const int j = g[u][i]; 46 | auto [v, c] = e[j]; 47 | if (c > 0 && h[v] == h[u] + 1) { 48 | auto a = dfs(v, t, std::min(r, c)); 49 | e[j].cap -= a; 50 | e[j ^ 1].cap += a; 51 | r -= a; 52 | if (r == 0) { 53 | return f; 54 | } 55 | } 56 | } 57 | return f - r; 58 | } 59 | void addEdge(int u, int v, T c) { 60 | g[u].push_back(e.size()); 61 | e.emplace_back(v, c); 62 | g[v].push_back(e.size()); 63 | e.emplace_back(u, 0); 64 | } 65 | T maxFlow(int s, int t) { 66 | T ans = 0; 67 | while (bfs(s, t)) { 68 | cur.assign(n, 0); 69 | ans += dfs(s, t, std::numeric_limits::max()); 70 | } 71 | return ans; 72 | } 73 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/02 - 图与网络/06B - 最大流(Flow 旧版其二,浮点数应用).cpp: -------------------------------------------------------------------------------- 1 | /** 最大流(Flow 旧版其二,浮点数应用) 2 | * 2022-04-09: https://cf.dianhsu.com/gym/104288/submission/201412765 3 | **/ 4 | template 5 | struct Flow { 6 | const int n; 7 | struct Edge { 8 | int to; 9 | T cap; 10 | Edge(int to, T cap) : to(to), cap(cap) {} 11 | }; 12 | std::vector e; 13 | std::vector> g; 14 | std::vector cur, h; 15 | Flow(int n) : n(n), g(n) {} 16 | 17 | bool bfs(int s, int t) { 18 | h.assign(n, -1); 19 | std::queue que; 20 | h[s] = 0; 21 | que.push(s); 22 | while (!que.empty()) { 23 | const int u = que.front(); 24 | que.pop(); 25 | for (int i : g[u]) { 26 | auto [v, c] = e[i]; 27 | if (c > 0 && h[v] == -1) { 28 | h[v] = h[u] + 1; 29 | if (v == t) { 30 | return true; 31 | } 32 | que.push(v); 33 | } 34 | } 35 | } 36 | return false; 37 | } 38 | 39 | T dfs(int u, int t, T f) { 40 | if (u == t) { 41 | return f; 42 | } 43 | auto r = f; 44 | double res = 0; 45 | for (int &i = cur[u]; i < int(g[u].size()); ++i) { 46 | const int j = g[u][i]; 47 | auto [v, c] = e[j]; 48 | if (c > 0 && h[v] == h[u] + 1) { 49 | auto a = dfs(v, t, std::min(r, c)); 50 | res += a; 51 | e[j].cap -= a; 52 | e[j ^ 1].cap += a; 53 | r -= a; 54 | if (r == 0) { 55 | return f; 56 | } 57 | } 58 | } 59 | return res; 60 | } 61 | void addEdge(int u, int v, T c) { 62 | g[u].push_back(e.size()); 63 | e.emplace_back(v, c); 64 | g[v].push_back(e.size()); 65 | e.emplace_back(u, 0); 66 | } 67 | T maxFlow(int s, int t) { 68 | T ans = 0; 69 | while (bfs(s, t)) { 70 | cur.assign(n, 0); 71 | ans += dfs(s, t, 1E100); 72 | } 73 | return ans; 74 | } 75 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/02 - 图与网络/06C - 最大流(MaxFlow 新版).cpp: -------------------------------------------------------------------------------- 1 | /** 最大流(MaxFlow 新版) 2 | * 2023-07-21: https://ac.nowcoder.com/acm/contest/view-submission?submissionId=62915815 3 | **/ 4 | constexpr int inf = 1E9; 5 | template 6 | struct MaxFlow { 7 | struct _Edge { 8 | int to; 9 | T cap; 10 | _Edge(int to, T cap) : to(to), cap(cap) {} 11 | }; 12 | 13 | int n; 14 | std::vector<_Edge> e; 15 | std::vector> g; 16 | std::vector cur, h; 17 | 18 | MaxFlow() {} 19 | MaxFlow(int n) { 20 | init(n); 21 | } 22 | 23 | void init(int n) { 24 | this->n = n; 25 | e.clear(); 26 | g.assign(n, {}); 27 | cur.resize(n); 28 | h.resize(n); 29 | } 30 | 31 | bool bfs(int s, int t) { 32 | h.assign(n, -1); 33 | std::queue que; 34 | h[s] = 0; 35 | que.push(s); 36 | while (!que.empty()) { 37 | const int u = que.front(); 38 | que.pop(); 39 | for (int i : g[u]) { 40 | auto [v, c] = e[i]; 41 | if (c > 0 && h[v] == -1) { 42 | h[v] = h[u] + 1; 43 | if (v == t) { 44 | return true; 45 | } 46 | que.push(v); 47 | } 48 | } 49 | } 50 | return false; 51 | } 52 | 53 | T dfs(int u, int t, T f) { 54 | if (u == t) { 55 | return f; 56 | } 57 | auto r = f; 58 | for (int &i = cur[u]; i < int(g[u].size()); ++i) { 59 | const int j = g[u][i]; 60 | auto [v, c] = e[j]; 61 | if (c > 0 && h[v] == h[u] + 1) { 62 | auto a = dfs(v, t, std::min(r, c)); 63 | e[j].cap -= a; 64 | e[j ^ 1].cap += a; 65 | r -= a; 66 | if (r == 0) { 67 | return f; 68 | } 69 | } 70 | } 71 | return f - r; 72 | } 73 | void addEdge(int u, int v, T c) { 74 | g[u].push_back(e.size()); 75 | e.emplace_back(v, c); 76 | g[v].push_back(e.size()); 77 | e.emplace_back(u, 0); 78 | } 79 | T flow(int s, int t) { 80 | T ans = 0; 81 | while (bfs(s, t)) { 82 | cur.assign(n, 0); 83 | ans += dfs(s, t, std::numeric_limits::max()); 84 | } 85 | return ans; 86 | } 87 | 88 | std::vector minCut() { 89 | std::vector c(n); 90 | for (int i = 0; i < n; i++) { 91 | c[i] = (h[i] != -1); 92 | } 93 | return c; 94 | } 95 | 96 | struct Edge { 97 | int from; 98 | int to; 99 | T cap; 100 | T flow; 101 | }; 102 | std::vector edges() { 103 | std::vector a; 104 | for (int i = 0; i < e.size(); i += 2) { 105 | Edge x; 106 | x.from = e[i + 1].to; 107 | x.to = e[i].to; 108 | x.cap = e[i].cap + e[i + 1].cap; 109 | x.flow = e[i + 1].cap; 110 | a.push_back(x); 111 | } 112 | return a; 113 | } 114 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/02 - 图与网络/07A - 费用流(MCFGraph 旧版).cpp: -------------------------------------------------------------------------------- 1 | /** 费用流(MCFGraph 旧版) 2 | * 2022-12-12: https://codeforces.com/contest/1766/submission/184974697 3 | * 4 | * 下方为最小费用**最大流**模板,如需求解最小费用**可行流**,需要去除建边限制 5 | **/ 6 | struct MCFGraph { 7 | struct Edge { 8 | int v, c, f; 9 | Edge(int v, int c, int f) : v(v), c(c), f(f) {} 10 | }; 11 | const int n; 12 | std::vector e; 13 | std::vector> g; 14 | std::vector h, dis; 15 | std::vector pre; 16 | bool dijkstra(int s, int t) { 17 | dis.assign(n, std::numeric_limits::max()); 18 | pre.assign(n, -1); 19 | std::priority_queue, std::vector>, std::greater>> que; 20 | dis[s] = 0; 21 | que.emplace(0, s); 22 | while (!que.empty()) { 23 | i64 d = que.top().first; 24 | int u = que.top().second; 25 | que.pop(); 26 | if (dis[u] < d) continue; 27 | for (int i : g[u]) { 28 | int v = e[i].v; 29 | int c = e[i].c; 30 | int f = e[i].f; 31 | if (c > 0 && dis[v] > d + h[u] - h[v] + f) { 32 | dis[v] = d + h[u] - h[v] + f; 33 | pre[v] = i; 34 | que.emplace(dis[v], v); 35 | } 36 | } 37 | } 38 | return dis[t] != std::numeric_limits::max(); 39 | } 40 | MCFGraph(int n) : n(n), g(n) {} 41 | void addEdge(int u, int v, int c, int f) { 42 | // if (f < 0) { 43 | g[u].push_back(e.size()); 44 | e.emplace_back(v, 0, f); 45 | g[v].push_back(e.size()); 46 | e.emplace_back(u, c, -f); 47 | // } else { 48 | // g[u].push_back(e.size()); 49 | // e.emplace_back(v, c, f); 50 | // g[v].push_back(e.size()); 51 | // e.emplace_back(u, 0, -f); 52 | // } 53 | } 54 | std::pair flow(int s, int t) { 55 | int flow = 0; 56 | i64 cost = 0; 57 | h.assign(n, 0); 58 | while (dijkstra(s, t)) { 59 | for (int i = 0; i < n; ++i) h[i] += dis[i]; 60 | int aug = std::numeric_limits::max(); 61 | for (int i = t; i != s; i = e[pre[i] ^ 1].v) aug = std::min(aug, e[pre[i]].c); 62 | for (int i = t; i != s; i = e[pre[i] ^ 1].v) { 63 | e[pre[i]].c -= aug; 64 | e[pre[i] ^ 1].c += aug; 65 | } 66 | flow += aug; 67 | cost += i64(aug) * h[t]; 68 | } 69 | return std::make_pair(flow, cost); 70 | } 71 | }; 72 | 73 | -------------------------------------------------------------------------------- /03 - jiangly模板收集/02 - 图与网络/07B - 费用流(MinCostFlow 新版).cpp: -------------------------------------------------------------------------------- 1 | /** 费用流(MinCostFlow 新版) 2 | * 2023-11-09: https://qoj.ac/submission/244680 3 | **/ 4 | template 5 | struct MinCostFlow { 6 | struct _Edge { 7 | int to; 8 | T cap; 9 | T cost; 10 | _Edge(int to_, T cap_, T cost_) : to(to_), cap(cap_), cost(cost_) {} 11 | }; 12 | int n; 13 | std::vector<_Edge> e; 14 | std::vector> g; 15 | std::vector h, dis; 16 | std::vector pre; 17 | bool dijkstra(int s, int t) { 18 | dis.assign(n, std::numeric_limits::max()); 19 | pre.assign(n, -1); 20 | std::priority_queue, std::vector>, std::greater>> que; 21 | dis[s] = 0; 22 | que.emplace(0, s); 23 | while (!que.empty()) { 24 | T d = que.top().first; 25 | int u = que.top().second; 26 | que.pop(); 27 | if (dis[u] != d) { 28 | continue; 29 | } 30 | for (int i : g[u]) { 31 | int v = e[i].to; 32 | T cap = e[i].cap; 33 | T cost = e[i].cost; 34 | if (cap > 0 && dis[v] > d + h[u] - h[v] + cost) { 35 | dis[v] = d + h[u] - h[v] + cost; 36 | pre[v] = i; 37 | que.emplace(dis[v], v); 38 | } 39 | } 40 | } 41 | return dis[t] != std::numeric_limits::max(); 42 | } 43 | MinCostFlow() {} 44 | MinCostFlow(int n_) { 45 | init(n_); 46 | } 47 | void init(int n_) { 48 | n = n_; 49 | e.clear(); 50 | g.assign(n, {}); 51 | } 52 | void addEdge(int u, int v, T cap, T cost) { 53 | g[u].push_back(e.size()); 54 | e.emplace_back(v, cap, cost); 55 | g[v].push_back(e.size()); 56 | e.emplace_back(u, 0, -cost); 57 | } 58 | std::pair flow(int s, int t) { 59 | T flow = 0; 60 | T cost = 0; 61 | h.assign(n, 0); 62 | while (dijkstra(s, t)) { 63 | for (int i = 0; i < n; ++i) { 64 | h[i] += dis[i]; 65 | } 66 | T aug = std::numeric_limits::max(); 67 | for (int i = t; i != s; i = e[pre[i] ^ 1].to) { 68 | aug = std::min(aug, e[pre[i]].cap); 69 | } 70 | for (int i = t; i != s; i = e[pre[i] ^ 1].to) { 71 | e[pre[i]].cap -= aug; 72 | e[pre[i] ^ 1].cap += aug; 73 | } 74 | flow += aug; 75 | cost += aug * h[t]; 76 | } 77 | return std::make_pair(flow, cost); 78 | } 79 | struct Edge { 80 | int from; 81 | int to; 82 | T cap; 83 | T cost; 84 | T flow; 85 | }; 86 | std::vector edges() { 87 | std::vector a; 88 | for (int i = 0; i < e.size(); i += 2) { 89 | Edge x; 90 | x.from = e[i + 1].to; 91 | x.to = e[i].to; 92 | x.cap = e[i].cap + e[i + 1].cap; 93 | x.cost = e[i].cost; 94 | x.flow = e[i + 1].cap; 95 | a.push_back(x); 96 | } 97 | return a; 98 | } 99 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/02 - 图与网络/08 - 树链剖分(HLD).cpp: -------------------------------------------------------------------------------- 1 | /** 树链剖分(HLD) 2 | * 2023-08-31: https://codeforces.com/contest/1863/submission/221214363 3 | **/ 4 | struct HLD { 5 | int n; 6 | std::vector siz, top, dep, parent, in, out, seq; 7 | std::vector> adj; 8 | int cur; 9 | 10 | HLD() {} 11 | HLD(int n) { 12 | init(n); 13 | } 14 | void init(int n) { 15 | this->n = n; 16 | siz.resize(n); 17 | top.resize(n); 18 | dep.resize(n); 19 | parent.resize(n); 20 | in.resize(n); 21 | out.resize(n); 22 | seq.resize(n); 23 | cur = 0; 24 | adj.assign(n, {}); 25 | } 26 | void addEdge(int u, int v) { 27 | adj[u].push_back(v); 28 | adj[v].push_back(u); 29 | } 30 | void work(int root = 0) { 31 | top[root] = root; 32 | dep[root] = 0; 33 | parent[root] = -1; 34 | dfs1(root); 35 | dfs2(root); 36 | } 37 | void dfs1(int u) { 38 | if (parent[u] != -1) { 39 | adj[u].erase(std::find(adj[u].begin(), adj[u].end(), parent[u])); 40 | } 41 | 42 | siz[u] = 1; 43 | for (auto &v : adj[u]) { 44 | parent[v] = u; 45 | dep[v] = dep[u] + 1; 46 | dfs1(v); 47 | siz[u] += siz[v]; 48 | if (siz[v] > siz[adj[u][0]]) { 49 | std::swap(v, adj[u][0]); 50 | } 51 | } 52 | } 53 | void dfs2(int u) { 54 | in[u] = cur++; 55 | seq[in[u]] = u; 56 | for (auto v : adj[u]) { 57 | top[v] = v == adj[u][0] ? top[u] : v; 58 | dfs2(v); 59 | } 60 | out[u] = cur; 61 | } 62 | int lca(int u, int v) { 63 | while (top[u] != top[v]) { 64 | if (dep[top[u]] > dep[top[v]]) { 65 | u = parent[top[u]]; 66 | } else { 67 | v = parent[top[v]]; 68 | } 69 | } 70 | return dep[u] < dep[v] ? u : v; 71 | } 72 | 73 | int dist(int u, int v) { 74 | return dep[u] + dep[v] - 2 * dep[lca(u, v)]; 75 | } 76 | 77 | int jump(int u, int k) { 78 | if (dep[u] < k) { 79 | return -1; 80 | } 81 | 82 | int d = dep[u] - k; 83 | 84 | while (dep[top[u]] > d) { 85 | u = parent[top[u]]; 86 | } 87 | 88 | return seq[in[u] - dep[u] + d]; 89 | } 90 | 91 | bool isAncester(int u, int v) { 92 | return in[u] <= in[v] && in[v] < out[u]; 93 | } 94 | 95 | int rootedParent(int u, int v) { 96 | std::swap(u, v); 97 | if (u == v) { 98 | return u; 99 | } 100 | if (!isAncester(u, v)) { 101 | return parent[u]; 102 | } 103 | auto it = std::upper_bound(adj[u].begin(), adj[u].end(), v, [&](int x, int y) { 104 | return in[x] < in[y]; 105 | }) - 1; 106 | return *it; 107 | } 108 | 109 | int rootedSize(int u, int v) { 110 | if (u == v) { 111 | return n; 112 | } 113 | if (!isAncester(v, u)) { 114 | return siz[v]; 115 | } 116 | return n - siz[rootedParent(u, v)]; 117 | } 118 | 119 | int rootedLca(int a, int b, int c) { 120 | return lca(a, b) ^ lca(b, c) ^ lca(c, a); 121 | } 122 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/02 - 图与网络/09 - 圆方树.cpp: -------------------------------------------------------------------------------- 1 | /** 圆方树(BlockCutTree) 2 | * -: - 3 | * * 感谢 [cnblogs @Misty7](https://www.luogu.com/user/344543) 提供 4 | **/ 5 | struct BlockCutTree { 6 | int n; 7 | std::vector> adj; 8 | std::vector dfn, low, stk; 9 | int cnt, cur; 10 | std::vector> edges; 11 | 12 | BlockCutTree() {} 13 | BlockCutTree(int n) { 14 | init(n); 15 | } 16 | 17 | void init(int n) { 18 | this->n = n; 19 | adj.assign(n, {}); 20 | dfn.assign(n, -1); 21 | low.resize(n); 22 | stk.clear(); 23 | cnt = cur = 0; 24 | edges.clear(); 25 | } 26 | 27 | void addEdge(int u, int v) { 28 | adj[u].push_back(v); 29 | adj[v].push_back(u); 30 | } 31 | 32 | void dfs(int x) { 33 | stk.push_back(x); 34 | dfn[x] = low[x] = cur++; 35 | 36 | for (auto y : adj[x]) { 37 | if (dfn[y] == -1) { 38 | dfs(y); 39 | low[x] = std::min(low[x], low[y]); 40 | if (low[y] == dfn[x]) { 41 | int v; 42 | do { 43 | v = stk.back(); 44 | stk.pop_back(); 45 | edges.emplace_back(n + cnt, v); 46 | } while (v != y); 47 | edges.emplace_back(x, n + cnt); 48 | cnt++; 49 | } 50 | } else { 51 | low[x] = std::min(low[x], dfn[y]); 52 | } 53 | } 54 | } 55 | 56 | std::pair>> work() { 57 | for (int i = 0; i < n; i++) { 58 | if (dfn[i] == -1) { 59 | stk.clear(); 60 | dfs(i); 61 | } 62 | } 63 | return {cnt, edges}; 64 | } 65 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/01 - 快速幂.cpp: -------------------------------------------------------------------------------- 1 | /** 快速幂 - 普通版 2 | * 2023-10-09: https://atcoder.jp/contests/tenka1-2017/submissions/46411797 3 | **/ 4 | int power(int a, i64 b, int p) { 5 | int res = 1; 6 | for (; b; b /= 2, a = 1LL * a * a % p) { 7 | if (b % 2) { 8 | res = 1LL * res * a % p; 9 | } 10 | } 11 | return res; 12 | } 13 | 14 | /** 快速幂 - 手写乘法 15 | * 2023-09-27: https://qoj.ac/submission/189343 16 | **/ 17 | using i64 = long long; 18 | 19 | i64 mul(i64 a, i64 b, i64 p) { 20 | i64 c = a * b - i64(1.0L * a * b / p) * p; 21 | c %= p; 22 | if (c < 0) { 23 | c += p; 24 | } 25 | return c; 26 | } 27 | 28 | i64 power(i64 a, i64 b, i64 p) { 29 | i64 res = 1; 30 | for (; b; b /= 2, a = mul(a, a, p)) { 31 | if (b % 2) { 32 | res = mul(res, a, p); 33 | } 34 | } 35 | return res; 36 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/02 - 基姆拉尔森公式.cpp: -------------------------------------------------------------------------------- 1 | /** 基姆拉尔森公式 2 | * 2023-09-05: https://qoj.ac/submission/164735 3 | **/ 4 | const int d[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 5 | 6 | bool isLeap(int y) { 7 | return y % 400 == 0 || (y % 4 == 0 && y % 100 != 0); 8 | } 9 | 10 | int daysInMonth(int y, int m) { 11 | return d[m - 1] + (isLeap(y) && m == 2); 12 | } 13 | 14 | int getDay(int y, int m, int d) { 15 | int ans = 0; 16 | for (int i = 1970; i < y; i++) { 17 | ans += 365 + isLeap(i); 18 | } 19 | for (int i = 1; i < m; i++) { 20 | ans += daysInMonth(y, i); 21 | } 22 | ans += d; 23 | return (ans + 2) % 7 + 1; 24 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/03 - 欧拉筛.cpp: -------------------------------------------------------------------------------- 1 | /** 欧拉筛 2 | * 2023-11-14: https://qoj.ac/submission/251234 3 | **/ 4 | std::vector minp, primes; 5 | 6 | void sieve(int n) { 7 | minp.assign(n + 1, 0); 8 | primes.clear(); 9 | 10 | for (int i = 2; i <= n; i++) { 11 | if (minp[i] == 0) { 12 | minp[i] = i; 13 | primes.push_back(i); 14 | } 15 | 16 | for (auto p : primes) { 17 | if (i * p > n) { 18 | break; 19 | } 20 | minp[i * p] = p; 21 | if (p == minp[i]) { 22 | break; 23 | } 24 | } 25 | } 26 | } 27 | 28 | bool isprime(int n) { 29 | return minp[n] == n; 30 | } 31 | 32 | /** 欧拉筛 33 | * 2023-03-22: https://yukicoder.me/submissions/851524 34 | * 2024-12-08: https://codeforces.com/contest/2040/submission/295597316 35 | **/ 36 | std::vector minp, primes, phi; 37 | 38 | void sieve(int n) { 39 | minp.assign(n + 1, 0); 40 | primes.clear(); 41 | phi.resize(n + 1); 42 | phi[1] = 1; 43 | 44 | for (int i = 2; i <= n; i++) { 45 | if (minp[i] == 0) { 46 | minp[i] = i; 47 | primes.push_back(i); 48 | phi[i] = i - 1; 49 | } 50 | 51 | for (auto p : primes) { 52 | if (i * p > n) { 53 | break; 54 | } 55 | minp[i * p] = p; 56 | if (p == minp[i]) { 57 | phi[i * p] = phi[i] * p; 58 | break; 59 | } 60 | phi[i * p] = phi[i] * (p - 1); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/04 - 莫比乌斯函数筛(莫比乌斯反演).cpp: -------------------------------------------------------------------------------- 1 | /** 莫比乌斯函数筛(莫比乌斯反演) 2 | * 2023-03-04: https://atcoder.jp/contests/tupc2022/submissions/39391116 3 | * 2023-04-07: https://yukicoder.me/submissions/857472 4 | **/ 5 | std::unordered_map fMu; 6 | 7 | std::vector minp, primes, phi, mu; 8 | std::vector sphi; 9 | 10 | void sieve(int n) { 11 | minp.assign(n + 1, 0); 12 | phi.assign(n + 1, 0); 13 | sphi.assign(n + 1, 0); 14 | mu.assign(n + 1, 0); 15 | primes.clear(); 16 | phi[1] = 1; 17 | mu[1] = 1; 18 | 19 | for (int i = 2; i <= n; i++) { 20 | if (minp[i] == 0) { 21 | minp[i] = i; 22 | phi[i] = i - 1; 23 | mu[i] = -1; 24 | primes.push_back(i); 25 | } 26 | 27 | for (auto p : primes) { 28 | if (i * p > n) { 29 | break; 30 | } 31 | minp[i * p] = p; 32 | if (p == minp[i]) { 33 | phi[i * p] = phi[i] * p; 34 | break; 35 | } 36 | phi[i * p] = phi[i] * (p - 1); 37 | mu[i * p] = -mu[i]; 38 | } 39 | } 40 | 41 | for (int i = 1; i <= n; i++) { 42 | sphi[i] = sphi[i - 1] + phi[i]; 43 | mu[i] += mu[i - 1]; 44 | } 45 | } 46 | 47 | Z sumMu(int n) { 48 | if (n <= N) { 49 | return mu[n]; 50 | } 51 | if (fMu.count(n)) { 52 | return fMu[n]; 53 | } 54 | if (n == 0) { 55 | return 0; 56 | } 57 | Z ans = 1; 58 | for (int l = 2, r; l <= n; l = r + 1) { 59 | r = n / (n / l); 60 | ans -= (r - l + 1) * sumMu(n / l); 61 | } 62 | return ans; 63 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/05 - 扩展欧几里得(exgcd).cpp: -------------------------------------------------------------------------------- 1 | /** 扩展欧几里得(exgcd) 2 | * 2024-08-07: https://codeforces.com/contest/1993/submission/275110715 3 | **/ 4 | i64 exgcd(i64 a, i64 b, i64 &x, i64 &y) { 5 | if (b == 0) { 6 | x = 1; 7 | y = 0; 8 | return a; 9 | } 10 | i64 g = exgcd(b, a % b, y, x); 11 | y -= a / b * x; 12 | return g; 13 | } 14 | 15 | // ax + b = 0 (mod m) 16 | std::pair sol(i64 a, i64 b, i64 m) { 17 | assert(m > 0); 18 | b *= -1; 19 | i64 x, y; 20 | i64 g = exgcd(a, m, x, y); 21 | if (g < 0) { 22 | g *= -1; 23 | x *= -1; 24 | y *= -1; 25 | } 26 | if (b % g != 0) { 27 | return {-1, -1}; 28 | } 29 | x = x * (b / g) % (m / g); 30 | if (x < 0) { 31 | x += m / g; 32 | } 33 | return {x, m / g}; 34 | } 35 | 36 | /** 扩展欧几里得(exgcd) 37 | * 2023-09-05: https://qoj.ac/submission/165983 38 | **/ 39 | std::array exgcd(i64 a, i64 b) { 40 | if (!b) { 41 | return {a, 1, 0}; 42 | } 43 | auto [g, x, y] = exgcd(b, a % b); 44 | return {g, y, x - a / b * y}; 45 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/06A - 欧拉函数(求解单个数的欧拉函数).cpp: -------------------------------------------------------------------------------- 1 | /** 欧拉函数(求解单个数的欧拉函数) 2 | * 2023-10-09: https://atcoder.jp/contests/tenka1-2017/submissions/46411797 3 | **/ 4 | int phi(int n) { 5 | int res = n; 6 | for (int i = 2; i * i <= n; i++) { 7 | if (n % i == 0) { 8 | while (n % i == 0) { 9 | n /= i; 10 | } 11 | res = res / i * (i - 1); 12 | } 13 | } 14 | if (n > 1) { 15 | res = res / n * (n - 1); 16 | } 17 | return res; 18 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/06B - 欧拉函数(求解全部数的欧拉函数).cpp: -------------------------------------------------------------------------------- 1 | /** 欧拉函数(求解全部数的欧拉函数) 2 | * 2023-09-24: https://qoj.ac/submission/187055 3 | **/ 4 | constexpr int N = 1E7; 5 | constexpr int P = 1000003; 6 | 7 | bool isprime[N + 1]; 8 | int phi[N + 1]; 9 | std::vector primes; 10 | 11 | std::fill(isprime + 2, isprime + N + 1, true); 12 | phi[1] = 1; 13 | for (int i = 2; i <= N; i++) { 14 | if (isprime[i]) { 15 | primes.push_back(i); 16 | phi[i] = i - 1; 17 | } 18 | for (auto p : primes) { 19 | if (i * p > N) { 20 | break; 21 | } 22 | isprime[i * p] = false; 23 | if (i % p == 0) { 24 | phi[i * p] = phi[i] * p; 25 | break; 26 | } 27 | phi[i * p] = phi[i] * (p - 1); 28 | } 29 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/07A - 取模运算类(Z).cpp: -------------------------------------------------------------------------------- 1 | /** 取模运算类(Z) 2 | * 2022-06-12: https://codeforces.com/contest/1697/submission/160317720 3 | **/ 4 | constexpr int P = 998244353; 5 | using i64 = long long; 6 | // assume -P <= x < 2P 7 | int norm(int x) { 8 | if (x < 0) { 9 | x += P; 10 | } 11 | if (x >= P) { 12 | x -= P; 13 | } 14 | return x; 15 | } 16 | template 17 | T power(T a, i64 b) { 18 | T res = 1; 19 | for (; b; b /= 2, a *= a) { 20 | if (b % 2) { 21 | res *= a; 22 | } 23 | } 24 | return res; 25 | } 26 | struct Z { 27 | int x; 28 | Z(int x = 0) : x(norm(x)) {} 29 | Z(i64 x) : x(norm(x % P)) {} 30 | int val() const { 31 | return x; 32 | } 33 | Z operator-() const { 34 | return Z(norm(P - x)); 35 | } 36 | Z inv() const { 37 | assert(x != 0); 38 | return power(*this, P - 2); 39 | } 40 | Z &operator*=(const Z &rhs) { 41 | x = i64(x) * rhs.x % P; 42 | return *this; 43 | } 44 | Z &operator+=(const Z &rhs) { 45 | x = norm(x + rhs.x); 46 | return *this; 47 | } 48 | Z &operator-=(const Z &rhs) { 49 | x = norm(x - rhs.x); 50 | return *this; 51 | } 52 | Z &operator/=(const Z &rhs) { 53 | return *this *= rhs.inv(); 54 | } 55 | friend Z operator*(const Z &lhs, const Z &rhs) { 56 | Z res = lhs; 57 | res *= rhs; 58 | return res; 59 | } 60 | friend Z operator+(const Z &lhs, const Z &rhs) { 61 | Z res = lhs; 62 | res += rhs; 63 | return res; 64 | } 65 | friend Z operator-(const Z &lhs, const Z &rhs) { 66 | Z res = lhs; 67 | res -= rhs; 68 | return res; 69 | } 70 | friend Z operator/(const Z &lhs, const Z &rhs) { 71 | Z res = lhs; 72 | res /= rhs; 73 | return res; 74 | } 75 | friend std::istream &operator>>(std::istream &is, Z &a) { 76 | i64 v; 77 | is >> v; 78 | a = Z(v); 79 | return is; 80 | } 81 | friend std::ostream &operator<<(std::ostream &os, const Z &a) { 82 | return os << a.val(); 83 | } 84 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/08A - 组合数(小范围预处理,逆元+杨辉三角).cpp: -------------------------------------------------------------------------------- 1 | /** 组合数(小范围预处理,逆元+杨辉三角) 2 | * 2024-03-14: https://qoj.ac/submission/353877 3 | * 2023-10-06: https://qoj.ac/submission/203196 4 | **/ 5 | constexpr int P = 1000000007; 6 | constexpr int L = 10000; 7 | 8 | int fac[L + 1], invfac[L + 1]; 9 | int sumbinom[L + 1][7]; 10 | 11 | int binom(int n, int m) { 12 | if (n < m || m < 0) { 13 | return 0; 14 | } 15 | return 1LL * fac[n] * invfac[m] % P * invfac[n - m] % P; 16 | } 17 | 18 | int power(int a, int b) { 19 | int res = 1; 20 | for (; b; b /= 2, a = 1LL * a * a % P) { 21 | if (b % 2) { 22 | res = 1LL * res * a % P; 23 | } 24 | } 25 | return res; 26 | } 27 | 28 | int main() { 29 | fac[0] = 1; 30 | for (int i = 1; i <= L; i++) { 31 | fac[i] = 1LL * fac[i - 1] * i % P; 32 | } 33 | invfac[L] = power(fac[L], P - 2); 34 | for (int i = L; i; i--) { 35 | invfac[i - 1] = 1LL * invfac[i] * i % P; 36 | } 37 | 38 | sumbinom[0][0] = 1; 39 | for (int i = 1; i <= L; i++) { 40 | for (int j = 0; j < 7; j++) { 41 | sumbinom[i][j] = (sumbinom[i - 1][j] + sumbinom[i - 1][(j + 6) % 7]) % P; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/08B - 组合数(Comb, with. ModIntBase).cpp: -------------------------------------------------------------------------------- 1 | /** 组合数(Comb, with. ModIntBase) 2 | * 2024-08-06: https://codeforces.com/contest/1999/submission/274744751 3 | **/ 4 | struct Comb { 5 | int n; 6 | std::vector _fac; 7 | std::vector _invfac; 8 | std::vector _inv; 9 | 10 | Comb() : n{0}, _fac{1}, _invfac{1}, _inv{0} {} 11 | Comb(int n) : Comb() { 12 | init(n); 13 | } 14 | 15 | void init(int m) { 16 | if (m <= n) return; 17 | _fac.resize(m + 1); 18 | _invfac.resize(m + 1); 19 | _inv.resize(m + 1); 20 | 21 | for (int i = n + 1; i <= m; i++) { 22 | _fac[i] = _fac[i - 1] * i; 23 | } 24 | _invfac[m] = _fac[m].inv(); 25 | for (int i = m; i > n; i--) { 26 | _invfac[i - 1] = _invfac[i] * i; 27 | _inv[i] = _invfac[i] * _fac[i - 1]; 28 | } 29 | n = m; 30 | } 31 | 32 | Z fac(int m) { 33 | if (m > n) init(2 * m); 34 | return _fac[m]; 35 | } 36 | Z invfac(int m) { 37 | if (m > n) init(2 * m); 38 | return _invfac[m]; 39 | } 40 | Z inv(int m) { 41 | if (m > n) init(2 * m); 42 | return _inv[m]; 43 | } 44 | Z binom(int n, int m) { 45 | if (n < m || m < 0) return 0; 46 | return fac(n) * invfac(m) * invfac(n - m); 47 | } 48 | } comb; -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/09B - 平面几何(with. complex).cpp: -------------------------------------------------------------------------------- 1 | /** 平面几何(with. complex) 2 | * 2023-09-04: https://qoj.ac/submission/164445 3 | **/ 4 | using Point = std::complex; 5 | 6 | #define x real 7 | #define y imag 8 | 9 | long double dot(const Point &a, const Point &b) { 10 | return (std::conj(a) * b).x(); 11 | } 12 | 13 | long double cross(const Point &a, const Point &b) { 14 | return (std::conj(a) * b).y(); 15 | } 16 | 17 | long double length(const Point &a) { 18 | return std::sqrt(dot(a, a)); 19 | } 20 | 21 | long double dist(const Point &a, const Point &b) { 22 | return length(a - b); 23 | } 24 | 25 | long double get(const Point &a, const Point &b, const Point &c, const Point &d) { 26 | auto e = a + (b - a) * cross(c - a, d - a) / cross(b - a, d - c); 27 | return dist(d, e); 28 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/10 - 立体几何(Point).cpp: -------------------------------------------------------------------------------- 1 | /** 立体几何(Point) 2 | * 2023-09-25 (i64): https://qoj.ac/submission/188519 3 | * 2023-09-28 (double): https://qoj.ac/submission/190463 4 | **/ 5 | using i64 = long long; 6 | using real = double; 7 | 8 | struct Point { 9 | real x = 0; 10 | real y = 0; 11 | real z = 0; 12 | }; 13 | 14 | Point operator+(const Point &a, const Point &b) { 15 | return {a.x + b.x, a.y + b.y, a.z + b.z}; 16 | } 17 | 18 | Point operator-(const Point &a, const Point &b) { 19 | return {a.x - b.x, a.y - b.y, a.z - b.z}; 20 | } 21 | 22 | Point operator*(const Point &a, real b) { 23 | return {a.x * b, a.y * b, a.z * b}; 24 | } 25 | 26 | Point operator/(const Point &a, real b) { 27 | return {a.x / b, a.y / b, a.z / b}; 28 | } 29 | 30 | real length(const Point &a) { 31 | return std::hypot(a.x, a.y, a.z); 32 | } 33 | 34 | Point normalize(const Point &a) { 35 | real l = length(a); 36 | return {a.x / l, a.y / l, a.z / l}; 37 | } 38 | 39 | real getAng(real a, real b, real c) { 40 | return std::acos((a * a + b * b - c * c) / 2 / a / b); 41 | } 42 | 43 | std::ostream &operator<<(std::ostream &os, const Point &a) { 44 | return os << "(" << a.x << ", " << a.y << ", " << a.z << ")"; 45 | } 46 | 47 | real dot(const Point &a, const Point &b) { 48 | return a.x * b.x + a.y * b.y + a.z * b.z; 49 | } 50 | 51 | Point cross(const Point &a, const Point &b) { 52 | return { 53 | a.y * b.z - a.z * b.y, 54 | a.z * b.x - a.x * b.z, 55 | a.x * b.y - a.y * b.x 56 | }; 57 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/11A - 静态凸包(with. Point,旧版).cpp: -------------------------------------------------------------------------------- 1 | /** 静态凸包(with. Point,旧版) 2 | * 2023-04-09: https://cf.dianhsu.com/gym/104288/submission/201412835 3 | **/ 4 | struct Point { 5 | i64 x; 6 | i64 y; 7 | Point(i64 x = 0, i64 y = 0) : x(x), y(y) {} 8 | }; 9 | 10 | bool operator==(const Point &a, const Point &b) { 11 | return a.x == b.x && a.y == b.y; 12 | } 13 | 14 | Point operator+(const Point &a, const Point &b) { 15 | return Point(a.x + b.x, a.y + b.y); 16 | } 17 | 18 | Point operator-(const Point &a, const Point &b) { 19 | return Point(a.x - b.x, a.y - b.y); 20 | } 21 | 22 | i64 dot(const Point &a, const Point &b) { 23 | return a.x * b.x + a.y * b.y; 24 | } 25 | 26 | i64 cross(const Point &a, const Point &b) { 27 | return a.x * b.y - a.y * b.x; 28 | } 29 | 30 | void norm(std::vector &h) { 31 | int i = 0; 32 | for (int j = 0; j < int(h.size()); j++) { 33 | if (h[j].y < h[i].y || (h[j].y == h[i].y && h[j].x < h[i].x)) { 34 | i = j; 35 | } 36 | } 37 | std::rotate(h.begin(), h.begin() + i, h.end()); 38 | } 39 | 40 | int sgn(const Point &a) { 41 | return a.y > 0 || (a.y == 0 && a.x > 0) ? 0 : 1; 42 | } 43 | 44 | std::vector getHull(std::vector p) { 45 | std::vector h, l; 46 | std::sort(p.begin(), p.end(), [&](auto a, auto b) { 47 | if (a.x != b.x) { 48 | return a.x < b.x; 49 | } else { 50 | return a.y < b.y; 51 | } 52 | }); 53 | p.erase(std::unique(p.begin(), p.end()), p.end()); 54 | if (p.size() <= 1) { 55 | return p; 56 | } 57 | 58 | for (auto a : p) { 59 | while (h.size() > 1 && cross(a - h.back(), a - h[h.size() - 2]) <= 0) { 60 | h.pop_back(); 61 | } 62 | while (l.size() > 1 && cross(a - l.back(), a - l[l.size() - 2]) >= 0) { 63 | l.pop_back(); 64 | } 65 | l.push_back(a); 66 | h.push_back(a); 67 | } 68 | 69 | l.pop_back(); 70 | std::reverse(h.begin(), h.end()); 71 | h.pop_back(); 72 | l.insert(l.end(), h.begin(), h.end()); 73 | return l; 74 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/11B - 静态凸包(with. Point,新版).cpp: -------------------------------------------------------------------------------- 1 | /** 静态凸包(with. Point,新版) 2 | * 2024-04-06: https://qoj.ac/submission/379920) 3 | **/ 4 | struct Point { 5 | i64 x; 6 | i64 y; 7 | Point() : x{0}, y{0} {} 8 | Point(i64 x_, i64 y_) : x{x_}, y{y_} {} 9 | }; 10 | 11 | i64 dot(Point a, Point b) { 12 | return a.x * b.x + a.y * b.y; 13 | } 14 | 15 | i64 cross(Point a, Point b) { 16 | return a.x * b.y - a.y * b.x; 17 | } 18 | 19 | Point operator+(Point a, Point b) { 20 | return Point(a.x + b.x, a.y + b.y); 21 | } 22 | 23 | Point operator-(Point a, Point b) { 24 | return Point(a.x - b.x, a.y - b.y); 25 | } 26 | 27 | auto getHull(std::vector p) { 28 | std::sort(p.begin(), p.end(), 29 | [&](auto a, auto b) { 30 | return a.x < b.x || (a.x == b.x && a.y < b.y); 31 | }); 32 | 33 | std::vector hi, lo; 34 | for (auto p : p) { 35 | while (hi.size() > 1 && cross(hi.back() - hi[hi.size() - 2], p - hi.back()) >= 0) { 36 | hi.pop_back(); 37 | } 38 | while (!hi.empty() && hi.back().x == p.x) { 39 | hi.pop_back(); 40 | } 41 | hi.push_back(p); 42 | while (lo.size() > 1 && cross(lo.back() - lo[lo.size() - 2], p - lo.back()) <= 0) { 43 | lo.pop_back(); 44 | } 45 | if (lo.empty() || lo.back().x < p.x) { 46 | lo.push_back(p); 47 | } 48 | } 49 | return std::make_pair(hi, lo); 50 | } 51 | 52 | const double inf = INFINITY; -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/11C - 静态凸包(with. complex).cpp: -------------------------------------------------------------------------------- 1 | /** 静态凸包(with. complex) 2 | * 2022-02-04: https://loj.ac/s/1370861 3 | **/ 4 | using Point = std::complex; 5 | 6 | #define x real 7 | #define y imag 8 | 9 | auto dot(const Point &a, const Point &b) { 10 | return (std::conj(a) * b).x(); 11 | } 12 | 13 | auto cross(const Point &a, const Point &b) { 14 | return (std::conj(a) * b).y(); 15 | } 16 | 17 | auto rot(const Point &p) { 18 | return Point(-p.y(), p.x()); 19 | } 20 | 21 | auto complexHull(std::vector a) { 22 | std::sort(a.begin(), a.end(), [&](auto a, auto b) { 23 | if (a.x() != b.x()) { 24 | return a.x() < b.x(); 25 | } else { 26 | return a.y() < b.y(); 27 | } 28 | }); 29 | 30 | std::vector l, h; 31 | 32 | for (auto p : a) { 33 | while (l.size() > 1 && cross(l.back() - l[l.size() - 2], p - l.back()) <= 0) { 34 | l.pop_back(); 35 | } 36 | 37 | while (h.size() > 1 && cross(h.back() - h[h.size() - 2], p - h.back()) >= 0) { 38 | h.pop_back(); 39 | } 40 | 41 | l.push_back(p); 42 | h.push_back(p); 43 | } 44 | 45 | std::reverse(h.begin(), h.end()); 46 | 47 | h.insert(h.end(), l.begin() + 1, l.end() - 1); 48 | 49 | return h; 50 | } 51 | 52 | int sgn(Point p) { 53 | if (p.y() > 0 || (p.y() == 0 && p.x() < 0)) { 54 | return 0; 55 | } else { 56 | return 1; 57 | } 58 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/12D - 多项式乘法.cpp: -------------------------------------------------------------------------------- 1 | /** 多项式乘法 2 | * 2024-03-10: https://qoj.ac/submission/350298 3 | **/ 4 | constexpr int P = 998244353; 5 | 6 | int power(int a, int b) { 7 | int res = 1; 8 | for (; b; b /= 2, a = 1LL * a * a % P) { 9 | if (b % 2) { 10 | res = 1LL * res * a % P; 11 | } 12 | } 13 | return res; 14 | } 15 | 16 | std::vector rev, roots {0, 1}; 17 | 18 | void dft(std::vector &a) { 19 | int n = a.size(); 20 | if (int(rev.size()) != n) { 21 | int k = __builtin_ctz(n) - 1; 22 | rev.resize(n); 23 | for (int i = 0; i < n; i++) { 24 | rev[i] = rev[i >> 1] >> 1 | (i & 1) << k; 25 | } 26 | } 27 | for (int i = 0; i < n; i++) { 28 | if (rev[i] < i) { 29 | std::swap(a[i], a[rev[i]]); 30 | } 31 | } 32 | if (roots.size() < n) { 33 | int k = __builtin_ctz(roots.size()); 34 | roots.resize(n); 35 | while ((1 << k) < n) { 36 | int e = power(31, 1 << (__builtin_ctz(P - 1) - k - 1)); 37 | for (int i = 1 << (k - 1); i < (1 << k); i++) { 38 | roots[2 * i] = roots[i]; 39 | roots[2 * i + 1] = 1LL * roots[i] * e % P; 40 | } 41 | k++; 42 | } 43 | } 44 | 45 | for (int k = 1; k < n; k *= 2) { 46 | for (int i = 0; i < n; i += 2 * k) { 47 | for (int j = 0; j < k; j++) { 48 | int u = a[i + j]; 49 | int v = 1LL * a[i + j + k] * roots[k + j] % P; 50 | a[i + j] = (u + v) % P; 51 | a[i + j + k] = (u - v) % P; 52 | } 53 | } 54 | } 55 | } 56 | 57 | void idft(std::vector &a) { 58 | int n = a.size(); 59 | std::reverse(a.begin() + 1, a.end()); 60 | dft(a); 61 | int inv = (1 - P) / n; 62 | for (int i = 0; i < n; i++) { 63 | a[i] = 1LL * a[i] * inv % P; 64 | } 65 | } 66 | 67 | std::vector mul(std::vector a, std::vector b) { 68 | int n = 1, tot = a.size() + b.size() - 1; 69 | while (n < tot) { 70 | n *= 2; 71 | } 72 | if (tot < 128) { 73 | std::vector c(a.size() + b.size() - 1); 74 | for (int i = 0; i < a.size(); i++) { 75 | for (int j = 0; j < b.size(); j++) { 76 | c[i + j] = (c[i + j] + 1LL * a[i] * b[j]) % P; 77 | } 78 | } 79 | return c; 80 | } 81 | a.resize(n); 82 | b.resize(n); 83 | dft(a); 84 | dft(b); 85 | for (int i = 0; i < n; i++) { 86 | a[i] = 1LL * a[i] * b[i] % P; 87 | } 88 | idft(a); 89 | a.resize(tot); 90 | return a; 91 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/13A - 生成函数(q-int).cpp: -------------------------------------------------------------------------------- 1 | /** 生成函数(q-int) 2 | * 2023-09-04: https://qoj.ac/submission/163986 3 | **/ 4 | using i64 = long long; 5 | using i128 = __int128; 6 | 7 | i64 power(i64 a, i64 b, i64 p) { 8 | i64 res = 1; 9 | for (; b; b /= 2, a = i128(a) * a % p) { 10 | if (b % 2) { 11 | res = i128(res) * a % p; 12 | } 13 | } 14 | return res; 15 | } 16 | 17 | std::pair qint(int q, int n, int p) { 18 | q %= p; 19 | for (int x = 2; x * x <= n; x++) { 20 | if (n % x == 0) { 21 | auto [v1, e1] = qint(q, x, p); 22 | auto [v2, e2] = qint(power(q, x, p), n / x, p); 23 | return {1LL * v1 * v2 % p, e1 + e2}; 24 | } 25 | } 26 | if (q == 1) { 27 | if (n == p) { 28 | return {0, 1}; 29 | } 30 | return {n, 0}; 31 | } 32 | // std::cerr << q << " " << n << " " << p << "\n"; 33 | i64 v = 1 - power(q, n, 1LL * p * p); 34 | if (v < 0) { 35 | v += 1LL * p * p; 36 | } 37 | assert(v != 0); 38 | int inv = power(1 - q + p, p - 2, p); 39 | if (v % p == 0) { 40 | return {(v / p) * inv % p, 1}; 41 | } else { 42 | return {v % p * inv % p, 0}; 43 | } 44 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/13B - 生成函数(q-Binomial).cpp: -------------------------------------------------------------------------------- 1 | /** 生成函数(q-Binomial) 2 | * 2023-09-04: https://qoj.ac/submission/164128 3 | **/ 4 | int power(int a, int b, int p) { 5 | int res = 1; 6 | for (; b; b /= 2, a = 1LL * a * a % p) { 7 | if (b % 2) { 8 | res = 1LL * res * a % p; 9 | } 10 | } 11 | return res; 12 | } 13 | 14 | int qint(int n, int q, int p) { 15 | return 1LL * (power(q, n, p) - 1) * power(q - 1, p - 2, p) % p; 16 | } 17 | 18 | int qBinomial(int n, int k, int q, int p) { 19 | if (q == 0) { 20 | return 1; 21 | } 22 | int r = 0; 23 | int x = 1; 24 | do { 25 | x = 1LL * x * q % p; 26 | r++; 27 | } while (x != 1); 28 | 29 | if (n / r > k / r + (n - k) / r) { 30 | return 0; 31 | } 32 | int num = 1, den = 1; 33 | for (int i = 1; i <= k % r; i++) { 34 | num = 1LL * num * qint(n % r - i + 1, q, p) % p; 35 | den = 1LL * den * qint(i, q, p) % p; 36 | } 37 | n /= r, k /= r; 38 | while (n > 0 || k > 0) { 39 | if (n % p < k % p) { 40 | return 0; 41 | } 42 | for (int i = 1; i <= k % p; i++) { 43 | num = 1LL * num * (n % p - i + 1) % p; 44 | den = 1LL * den * i % p; 45 | } 46 | n /= p, k /= p; 47 | } 48 | int ans = 1LL * num * power(den, p - 2, p) % p; 49 | return ans; 50 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/14 - 自适应辛普森法(Simpson).cpp: -------------------------------------------------------------------------------- 1 | /** 自适应辛普森法(Simpson) 2 | * 2020-09-25: https://codeforces.com/gym/100198/submission/93747918 3 | * 2021-01-28: https://codeforces.com/gym/100553/submission/105639142 4 | * 2023-09-02: https://qoj.ac/submission/161388 5 | **/ 6 | const double Pi = std::acos(-1.0); 7 | constexpr double EPS = 1e-9; 8 | double v, r, d; 9 | double f(double x) { 10 | double s = std::sin(x); 11 | return 1 / v / (std::sqrt(s * s + 3) - s); 12 | } 13 | double simpson(double l, double r) { 14 | return (f(l) + 4 * f((l + r) / 2) + f(r)) * (r - l) / 6; 15 | } 16 | double integral(double l, double r, double eps, double st) { 17 | double mid = (l + r) / 2; 18 | double sl = simpson(l, mid); 19 | double sr = simpson(mid, r); 20 | if (std::abs(sl + sr - st) <= 15 * eps) 21 | return sl + sr + (sl + sr - st) / 15; 22 | return integral(l, mid, eps / 2, sl) + integral(mid, r, eps / 2, sr); 23 | } 24 | double integral(double l, double r) { 25 | return integral(l, r, EPS, simpson(l, r)); 26 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/15 - 矩阵(Matrix).cpp: -------------------------------------------------------------------------------- 1 | /** 矩阵(Matrix) 2 | * 2024-03-14: https://qoj.ac/submission/353771 3 | **/ 4 | using i64 = long long; 5 | using u64 = unsigned long long; 6 | 7 | using Matrix = std::array; 8 | 9 | Matrix operator*(const Matrix &a, const Matrix &b) { 10 | Matrix c{}; 11 | for (int i = 0; i <= 64; i++) { 12 | for (int j = 0; j <= 64; j++) { 13 | if (j == 64 ? i == 64 : (a[i] >> j & 1)) { 14 | c[i] ^= b[j]; 15 | } 16 | } 17 | } 18 | return c; 19 | } 20 | 21 | u64 operator*(u64 a, const Matrix &b) { 22 | u64 c = 0; 23 | for (int i = 0; i <= 64; i++) { 24 | if (i == 64 || (a >> i & 1)) { 25 | c ^= b[i]; 26 | } 27 | } 28 | return c; 29 | } 30 | 31 | Matrix readMatrix() { 32 | int m; 33 | std::cin >> m; 34 | 35 | Matrix f{}; 36 | for (int i = 0; i < m; i++) { 37 | int s, o; 38 | u64 A; 39 | std::cin >> s >> o >> A; 40 | 41 | if (o == 0) { 42 | for (int j = 0; j < 64; j++) { 43 | if (A >> ((j + s) % 64) & 1) { 44 | f[64] ^= 1ULL << ((j + s) % 64); 45 | } else { 46 | f[j] ^= 1ULL << ((j + s) % 64); 47 | } 48 | } 49 | } else { 50 | for (int j = 0; j < 64; j++) { 51 | if (A >> ((j + s) % 64) & 1) { 52 | f[j] ^= 1ULL << ((j + s) % 64); 53 | } 54 | } 55 | } 56 | } 57 | 58 | u64 B; 59 | std::cin >> B; 60 | f[64] ^= B; 61 | 62 | return f; 63 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/16 - 高斯消元法 gaussian elimination(gauss)【久远】.cpp: -------------------------------------------------------------------------------- 1 | /** 高斯消元法(gaussian elimination)【久远】 2 | * 2020-08-30: https://codeforces.com/gym/102129/submission/91334513 3 | **/ 4 | std::vector operator*(const std::vector &lhs, const std::vector &rhs) { 5 | std::vector res(lhs.size() + rhs.size() - 1); 6 | for (int i = 0; i < int(lhs.size()); ++i) 7 | for (int j = 0; j < int(rhs.size()); ++j) 8 | res[i + j] = (res[i + j] + 1ll * lhs[i] * rhs[j]) % P; 9 | return res; 10 | } 11 | std::vector operator%(const std::vector &lhs, const std::vector &rhs) { 12 | auto res = lhs; 13 | int m = rhs.size() - 1; 14 | int inv = power(rhs.back(), P - 2); 15 | for (int i = res.size() - 1; i >= m; --i) { 16 | int x = 1ll * inv * res[i] % P; 17 | for (int j = 0; j < m; ++j) 18 | res[i - m + j] = (res[i - m + j] + 1ll * (P - x) * rhs[j]) % P; 19 | } 20 | if (int(res.size()) > m) 21 | res.resize(m); 22 | return res; 23 | } 24 | std::vector gauss(std::vector> a, std::vector b) { 25 | int n = a.size(); 26 | for (int i = 0; i < n; ++i) { 27 | int r = i; 28 | while (a[r][i] == 0) 29 | ++r; 30 | std::swap(a[i], a[r]); 31 | std::swap(b[i], b[r]); 32 | int inv = power(a[i][i], P - 2); 33 | for (int j = i; j < n; ++j) 34 | a[i][j] = 1ll * a[i][j] * inv % P; 35 | b[i] = 1ll * b[i] * inv % P; 36 | for (int j = 0; j < n; ++j) { 37 | if (i == j) 38 | continue; 39 | int x = a[j][i]; 40 | for (int k = i; k < n; ++k) 41 | a[j][k] = (a[j][k] + 1ll * (P - x) * a[i][k]) % P; 42 | b[j] = (b[j] + 1ll * (P - x) * b[i]) % P; 43 | } 44 | } 45 | return b; 46 | } 47 | /** 高斯消元法(gaussian elimination)【久远】 48 | * 2020-12-02: https://www.codechef.com/viewsolution/39942900 49 | **/ 50 | std::vector gauss(std::vector> a, std::vector b) { 51 | int n = a.size(); 52 | for (int i = 0; i < n; ++i) { 53 | double x = a[i][i]; 54 | for (int j = i; j < n; ++j) a[i][j] /= x; 55 | b[i] /= x; 56 | for (int j = 0; j < n; ++j) { 57 | if (i == j) continue; 58 | x = a[j][i]; 59 | for (int k = i; k < n; ++k) a[j][k] -= a[i][k] * x; 60 | b[j] -= b[i] * x; 61 | } 62 | } 63 | return b; 64 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/03 - 数论、几何与多项式/17 - 素数测试与因式分解(Miller-Rabin & Pollard-Rho).cpp: -------------------------------------------------------------------------------- 1 | /** 素数测试与因式分解(Miller-Rabin & Pollard-Rho) 2 | * 2023-05-16: https://cf.dianhsu.com/gym/104354/submission/206130894 3 | **/ 4 | i64 mul(i64 a, i64 b, i64 m) { 5 | return static_cast<__int128>(a) * b % m; 6 | } 7 | i64 power(i64 a, i64 b, i64 m) { 8 | i64 res = 1 % m; 9 | for (; b; b >>= 1, a = mul(a, a, m)) 10 | if (b & 1) 11 | res = mul(res, a, m); 12 | return res; 13 | } 14 | bool isprime(i64 n) { 15 | if (n < 2) 16 | return false; 17 | static constexpr int A[] = {2, 3, 5, 7, 11, 13, 17, 19, 23}; 18 | int s = __builtin_ctzll(n - 1); 19 | i64 d = (n - 1) >> s; 20 | for (auto a : A) { 21 | if (a == n) 22 | return true; 23 | i64 x = power(a, d, n); 24 | if (x == 1 || x == n - 1) 25 | continue; 26 | bool ok = false; 27 | for (int i = 0; i < s - 1; ++i) { 28 | x = mul(x, x, n); 29 | if (x == n - 1) { 30 | ok = true; 31 | break; 32 | } 33 | } 34 | if (!ok) 35 | return false; 36 | } 37 | return true; 38 | } 39 | std::vector factorize(i64 n) { 40 | std::vector p; 41 | std::function f = [&](i64 n) { 42 | if (n <= 10000) { 43 | for (int i = 2; i * i <= n; ++i) 44 | for (; n % i == 0; n /= i) 45 | p.push_back(i); 46 | if (n > 1) 47 | p.push_back(n); 48 | return; 49 | } 50 | if (isprime(n)) { 51 | p.push_back(n); 52 | return; 53 | } 54 | auto g = [&](i64 x) { 55 | return (mul(x, x, n) + 1) % n; 56 | }; 57 | i64 x0 = 2; 58 | while (true) { 59 | i64 x = x0; 60 | i64 y = x0; 61 | i64 d = 1; 62 | i64 power = 1, lam = 0; 63 | i64 v = 1; 64 | while (d == 1) { 65 | y = g(y); 66 | ++lam; 67 | v = mul(v, std::abs(x - y), n); 68 | if (lam % 127 == 0) { 69 | d = std::gcd(v, n); 70 | v = 1; 71 | } 72 | if (power == lam) { 73 | x = y; 74 | power *= 2; 75 | lam = 0; 76 | d = std::gcd(v, n); 77 | v = 1; 78 | } 79 | } 80 | if (d != n) { 81 | f(d); 82 | f(n / d); 83 | return; 84 | } 85 | ++x0; 86 | } 87 | }; 88 | f(n); 89 | std::sort(p.begin(), p.end()); 90 | return p; 91 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/04 - 数据结构/01A - 树状数组(Fenwick 旧版).cpp: -------------------------------------------------------------------------------- 1 | /** 树状数组(Fenwick 旧版) 2 | * 2023-08-11: https://ac.nowcoder.com/acm/contest/view-submission?submissionId=63382128 3 | **/ 4 | template 5 | struct Fenwick { 6 | int n; 7 | std::vector a; 8 | 9 | Fenwick(int n = 0) { 10 | init(n); 11 | } 12 | 13 | void init(int n) { 14 | this->n = n; 15 | a.assign(n, T()); 16 | } 17 | 18 | void add(int x, T v) { 19 | for (int i = x + 1; i <= n; i += i & -i) { 20 | a[i - 1] += v; 21 | } 22 | } 23 | 24 | T sum(int x) { 25 | auto ans = T(); 26 | for (int i = x; i > 0; i -= i & -i) { 27 | ans += a[i - 1]; 28 | } 29 | return ans; 30 | } 31 | 32 | T rangeSum(int l, int r) { 33 | return sum(r) - sum(l); 34 | } 35 | 36 | int kth(T k) { 37 | int x = 0; 38 | for (int i = 1 << std::__lg(n); i; i /= 2) { 39 | if (x + i <= n && k >= a[x + i - 1]) { 40 | x += i; 41 | k -= a[x - 1]; 42 | } 43 | } 44 | return x; 45 | } 46 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/04 - 数据结构/01B - 树状数组(Fenwick 新版).cpp: -------------------------------------------------------------------------------- 1 | /** 树状数组(Fenwick 新版) 2 | * 2023-12-28: https://codeforces.com/contest/1915/submission/239262801 3 | **/ 4 | template 5 | struct Fenwick { 6 | int n; 7 | std::vector a; 8 | 9 | Fenwick(int n_ = 0) { 10 | init(n_); 11 | } 12 | 13 | void init(int n_) { 14 | n = n_; 15 | a.assign(n, T{}); 16 | } 17 | 18 | void add(int x, const T &v) { 19 | for (int i = x + 1; i <= n; i += i & -i) { 20 | a[i - 1] = a[i - 1] + v; 21 | } 22 | } 23 | 24 | T sum(int x) { 25 | T ans{}; 26 | for (int i = x; i > 0; i -= i & -i) { 27 | ans = ans + a[i - 1]; 28 | } 29 | return ans; 30 | } 31 | 32 | T rangeSum(int l, int r) { 33 | return sum(r) - sum(l); 34 | } 35 | 36 | int select(const T &k) { 37 | int x = 0; 38 | T cur{}; 39 | for (int i = 1 << std::__lg(n); i; i /= 2) { 40 | if (x + i <= n && cur + a[x + i - 1] <= k) { 41 | x += i; 42 | cur = cur + a[x - 1]; 43 | } 44 | } 45 | return x; 46 | } 47 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/04 - 数据结构/02A - 并查集(DSU).cpp: -------------------------------------------------------------------------------- 1 | /** 并查集(DSU) 2 | * 2023-08-04: https://ac.nowcoder.com/acm/contest/view-submission?submissionId=63239142 3 | **/ 4 | struct DSU { 5 | std::vector f, siz; 6 | 7 | DSU() {} 8 | DSU(int n) { 9 | init(n); 10 | } 11 | 12 | void init(int n) { 13 | f.resize(n); 14 | std::iota(f.begin(), f.end(), 0); 15 | siz.assign(n, 1); 16 | } 17 | 18 | int find(int x) { 19 | while (x != f[x]) { 20 | x = f[x] = f[f[x]]; 21 | } 22 | return x; 23 | } 24 | 25 | bool same(int x, int y) { 26 | return find(x) == find(y); 27 | } 28 | 29 | bool merge(int x, int y) { 30 | x = find(x); 31 | y = find(y); 32 | if (x == y) { 33 | return false; 34 | } 35 | siz[x] += siz[y]; 36 | f[y] = x; 37 | return true; 38 | } 39 | 40 | int size(int x) { 41 | return siz[find(x)]; 42 | } 43 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/04 - 数据结构/02B - 可撤销并查集(DSU With Rollback).cpp: -------------------------------------------------------------------------------- 1 | /** 可撤销并查集(DSU With Rollback) 2 | * 2024-09-17: https://qoj.ac/submission/569639 3 | **/ 4 | struct DSU { 5 | std::vector siz; 6 | std::vector f; 7 | std::vector> his; 8 | 9 | DSU(int n) : siz(n + 1, 1), f(n + 1) { 10 | std::iota(f.begin(), f.end(), 0); 11 | } 12 | 13 | int find(int x) { 14 | while (f[x] != x) { 15 | x = f[x]; 16 | } 17 | return x; 18 | } 19 | 20 | bool merge(int x, int y) { 21 | x = find(x); 22 | y = find(y); 23 | if (x == y) { 24 | return false; 25 | } 26 | if (siz[x] < siz[y]) { 27 | std::swap(x, y); 28 | } 29 | his.push_back({x, y}); 30 | siz[x] += siz[y]; 31 | f[y] = x; 32 | return true; 33 | } 34 | 35 | int time() { 36 | return his.size(); 37 | } 38 | 39 | void revert(int tm) { 40 | while (his.size() > tm) { 41 | auto [x, y] = his.back(); 42 | his.pop_back(); 43 | f[y] = y; 44 | siz[x] -= siz[y]; 45 | } 46 | } 47 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/04 - 数据结构/03A - 线段树(SegmentTree+Info 区间加+单点修改).cpp: -------------------------------------------------------------------------------- 1 | /** 线段树(SegmentTree+Info 区间加+单点修改) 2 | * 2023-09-13: https://qoj.ac/submission/178310 3 | **/ 4 | struct SegmentTree { 5 | int n; 6 | std::vector tag; 7 | std::vector info; 8 | SegmentTree(int n_) : n(n_), tag(4 * n), info(4 * n) {} 9 | 10 | void pull(int p) { 11 | info[p] = info[2 * p] + info[2 * p + 1]; 12 | } 13 | 14 | void add(int p, int v) { 15 | tag[p] += v; 16 | info[p].max += v; 17 | } 18 | 19 | void push(int p) { 20 | add(2 * p, tag[p]); 21 | add(2 * p + 1, tag[p]); 22 | tag[p] = 0; 23 | } 24 | 25 | Info query(int p, int l, int r, int x, int y) { 26 | if (l >= y || r <= x) { 27 | return {}; 28 | } 29 | if (l >= x && r <= y) { 30 | return info[p]; 31 | } 32 | int m = (l + r) / 2; 33 | push(p); 34 | return query(2 * p, l, m, x, y) + query(2 * p + 1, m, r, x, y); 35 | } 36 | 37 | Info query(int x, int y) { 38 | return query(1, 0, n, x, y); 39 | } 40 | 41 | void rangeAdd(int p, int l, int r, int x, int y, int v) { 42 | if (l >= y || r <= x) { 43 | return; 44 | } 45 | if (l >= x && r <= y) { 46 | return add(p, v); 47 | } 48 | int m = (l + r) / 2; 49 | push(p); 50 | rangeAdd(2 * p, l, m, x, y, v); 51 | rangeAdd(2 * p + 1, m, r, x, y, v); 52 | pull(p); 53 | } 54 | 55 | void rangeAdd(int x, int y, int v) { 56 | rangeAdd(1, 0, n, x, y, v); 57 | } 58 | 59 | void modify(int p, int l, int r, int x, const Info &v) { 60 | if (r - l == 1) { 61 | info[p] = v; 62 | return; 63 | } 64 | int m = (l + r) / 2; 65 | push(p); 66 | if (x < m) { 67 | modify(2 * p, l, m, x, v); 68 | } else { 69 | modify(2 * p + 1, m, r, x, v); 70 | } 71 | pull(p); 72 | } 73 | 74 | void modify(int x, const Info &v) { 75 | modify(1, 0, n, x, v); 76 | } 77 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/04 - 数据结构/03B - 线段树(SegmentTree 区间乘+单点加).cpp: -------------------------------------------------------------------------------- 1 | /** 线段树(SegmentTree 区间乘+单点加) 2 | * 2023-10-18: https://cf.dianhsu.com/gym/104417/submission/223800089 3 | **/ 4 | struct SegmentTree { 5 | int n; 6 | std::vector tag, sum; 7 | SegmentTree(int n_) : n(n_), tag(4 * n, 1), sum(4 * n) {} 8 | 9 | void pull(int p) { 10 | sum[p] = (sum[2 * p] + sum[2 * p + 1]) % P; 11 | } 12 | 13 | void mul(int p, int v) { 14 | tag[p] = 1LL * tag[p] * v % P; 15 | sum[p] = 1LL * sum[p] * v % P; 16 | } 17 | 18 | void push(int p) { 19 | mul(2 * p, tag[p]); 20 | mul(2 * p + 1, tag[p]); 21 | tag[p] = 1; 22 | } 23 | 24 | int query(int p, int l, int r, int x, int y) { 25 | if (l >= y || r <= x) { 26 | return 0; 27 | } 28 | if (l >= x && r <= y) { 29 | return sum[p]; 30 | } 31 | int m = (l + r) / 2; 32 | push(p); 33 | return (query(2 * p, l, m, x, y) + query(2 * p + 1, m, r, x, y)) % P; 34 | } 35 | 36 | int query(int x, int y) { 37 | return query(1, 0, n, x, y); 38 | } 39 | 40 | void rangeMul(int p, int l, int r, int x, int y, int v) { 41 | if (l >= y || r <= x) { 42 | return; 43 | } 44 | if (l >= x && r <= y) { 45 | return mul(p, v); 46 | } 47 | int m = (l + r) / 2; 48 | push(p); 49 | rangeMul(2 * p, l, m, x, y, v); 50 | rangeMul(2 * p + 1, m, r, x, y, v); 51 | pull(p); 52 | } 53 | 54 | void rangeMul(int x, int y, int v) { 55 | rangeMul(1, 0, n, x, y, v); 56 | } 57 | 58 | void add(int p, int l, int r, int x, int v) { 59 | if (r - l == 1) { 60 | sum[p] = (sum[p] + v) % P; 61 | return; 62 | } 63 | int m = (l + r) / 2; 64 | push(p); 65 | if (x < m) { 66 | add(2 * p, l, m, x, v); 67 | } else { 68 | add(2 * p + 1, m, r, x, v); 69 | } 70 | pull(p); 71 | } 72 | 73 | void add(int x, int v) { 74 | add(1, 0, n, x, v); 75 | } 76 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/04 - 数据结构/03D - 线段树(SegmentTree+Info+Merge 初始赋值+单点修改+区间合并).cpp: -------------------------------------------------------------------------------- 1 | /** 线段树(SegmentTree+Info+Merge 初始赋值+单点修改+区间合并) 2 | * 2022-04-23: https://codeforces.com/contest/1672/submission/154766851 3 | **/ 4 | template> 6 | struct SegmentTree { 7 | const int n; 8 | const Merge merge; 9 | std::vector info; 10 | SegmentTree(int n) : n(n), merge(Merge()), info(4 << std::__lg(n)) {} 11 | SegmentTree(std::vector init) : SegmentTree(init.size()) { 12 | std::function build = [&](int p, int l, int r) { 13 | if (r - l == 1) { 14 | info[p] = init[l]; 15 | return; 16 | } 17 | int m = (l + r) / 2; 18 | build(2 * p, l, m); 19 | build(2 * p + 1, m, r); 20 | pull(p); 21 | }; 22 | build(1, 0, n); 23 | } 24 | void pull(int p) { 25 | info[p] = merge(info[2 * p], info[2 * p + 1]); 26 | } 27 | void modify(int p, int l, int r, int x, const Info &v) { 28 | if (r - l == 1) { 29 | info[p] = v; 30 | return; 31 | } 32 | int m = (l + r) / 2; 33 | if (x < m) { 34 | modify(2 * p, l, m, x, v); 35 | } else { 36 | modify(2 * p + 1, m, r, x, v); 37 | } 38 | pull(p); 39 | } 40 | void modify(int p, const Info &v) { 41 | modify(1, 0, n, p, v); 42 | } 43 | Info rangeQuery(int p, int l, int r, int x, int y) { 44 | if (l >= y || r <= x) { 45 | return Info(); 46 | } 47 | if (l >= x && r <= y) { 48 | return info[p]; 49 | } 50 | int m = (l + r) / 2; 51 | return merge(rangeQuery(2 * p, l, m, x, y), rangeQuery(2 * p + 1, m, r, x, y)); 52 | } 53 | Info rangeQuery(int l, int r) { 54 | return rangeQuery(1, 0, n, l, r); 55 | } 56 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/04 - 数据结构/05 - 高精度(BigInt).cpp: -------------------------------------------------------------------------------- 1 | /** 高精度(BigInt) 2 | * 2023-09-11: https://qoj.ac/submission/176420 3 | **/ 4 | constexpr int N = 1000; 5 | 6 | struct BigInt { 7 | int a[N]; 8 | BigInt(int x = 0) : a{} { 9 | for (int i = 0; x; i++) { 10 | a[i] = x % 10; 11 | x /= 10; 12 | } 13 | } 14 | BigInt &operator*=(int x) { 15 | for (int i = 0; i < N; i++) { 16 | a[i] *= x; 17 | } 18 | for (int i = 0; i < N - 1; i++) { 19 | a[i + 1] += a[i] / 10; 20 | a[i] %= 10; 21 | } 22 | return *this; 23 | } 24 | BigInt &operator/=(int x) { 25 | for (int i = N - 1; i >= 0; i--) { 26 | if (i) { 27 | a[i - 1] += a[i] % x * 10; 28 | } 29 | a[i] /= x; 30 | } 31 | return *this; 32 | } 33 | BigInt &operator+=(const BigInt &x) { 34 | for (int i = 0; i < N; i++) { 35 | a[i] += x.a[i]; 36 | if (a[i] >= 10) { 37 | a[i + 1] += 1; 38 | a[i] -= 10; 39 | } 40 | } 41 | return *this; 42 | } 43 | }; 44 | 45 | std::ostream &operator<<(std::ostream &o, const BigInt &a) { 46 | int t = N - 1; 47 | while (a.a[t] == 0) { 48 | t--; 49 | } 50 | for (int i = t; i >= 0; i--) { 51 | o << a.a[i]; 52 | } 53 | return o; 54 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/04 - 数据结构/06 - 状压RMQ(RMQ).cpp: -------------------------------------------------------------------------------- 1 | /** 状压RMQ(RMQ) 2 | * 2023-03-02: https://atcoder.jp/contests/joi2022ho/submissions/39351739 3 | * 2023-09-04: https://qoj.ac/submission/163598 4 | * 2024-08-07: https://atcoder.jp/contests/abc365/submissions/56438692 5 | **/ 6 | template> 8 | struct RMQ { 9 | const Cmp cmp = Cmp(); 10 | static constexpr unsigned B = 64; 11 | using u64 = unsigned long long; 12 | int n; 13 | std::vector> a; 14 | std::vector pre, suf, ini; 15 | std::vector stk; 16 | RMQ() {} 17 | RMQ(const std::vector &v) { 18 | init(v); 19 | } 20 | void init(const std::vector &v) { 21 | n = v.size(); 22 | pre = suf = ini = v; 23 | stk.resize(n); 24 | if (!n) { 25 | return; 26 | } 27 | const int M = (n - 1) / B + 1; 28 | const int lg = std::__lg(M); 29 | a.assign(lg + 1, std::vector(M)); 30 | for (int i = 0; i < M; i++) { 31 | a[0][i] = v[i * B]; 32 | for (int j = 1; j < B && i * B + j < n; j++) { 33 | a[0][i] = std::min(a[0][i], v[i * B + j], cmp); 34 | } 35 | } 36 | for (int i = 1; i < n; i++) { 37 | if (i % B) { 38 | pre[i] = std::min(pre[i], pre[i - 1], cmp); 39 | } 40 | } 41 | for (int i = n - 2; i >= 0; i--) { 42 | if (i % B != B - 1) { 43 | suf[i] = std::min(suf[i], suf[i + 1], cmp); 44 | } 45 | } 46 | for (int j = 0; j < lg; j++) { 47 | for (int i = 0; i + (2 << j) <= M; i++) { 48 | a[j + 1][i] = std::min(a[j][i], a[j][i + (1 << j)], cmp); 49 | } 50 | } 51 | for (int i = 0; i < M; i++) { 52 | const int l = i * B; 53 | const int r = std::min(1U * n, l + B); 54 | u64 s = 0; 55 | for (int j = l; j < r; j++) { 56 | while (s && cmp(v[j], v[std::__lg(s) + l])) { 57 | s ^= 1ULL << std::__lg(s); 58 | } 59 | s |= 1ULL << (j - l); 60 | stk[j] = s; 61 | } 62 | } 63 | } 64 | T operator()(int l, int r) { 65 | if (l / B != (r - 1) / B) { 66 | T ans = std::min(suf[l], pre[r - 1], cmp); 67 | l = l / B + 1; 68 | r = r / B; 69 | if (l < r) { 70 | int k = std::__lg(r - l); 71 | ans = std::min({ans, a[k][l], a[k][r - (1 << k)]}, cmp); 72 | } 73 | return ans; 74 | } else { 75 | int x = B * (l / B); 76 | return ini[__builtin_ctzll(stk[r - 1] >> (l - x)) + l]; 77 | } 78 | } 79 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/04 - 数据结构/08A - 其他平衡树.cpp: -------------------------------------------------------------------------------- 1 | /** 其他平衡树 2 | * 2023-08-04: https://ac.nowcoder.com/acm/contest/view-submission?submissionId=63246177 3 | **/ 4 | struct Node { 5 | Node *l = nullptr; 6 | Node *r = nullptr; 7 | int sum = 0; 8 | int sumodd = 0; 9 | 10 | Node(Node *t) { 11 | if (t) { 12 | *this = *t; 13 | } 14 | } 15 | }; 16 | 17 | Node *add(Node *t, int l, int r, int x, int v) { 18 | t = new Node(t); 19 | t->sum += v; 20 | t->sumodd += (x % 2) * v; 21 | if (r - l == 1) { 22 | return t; 23 | } 24 | int m = (l + r) / 2; 25 | if (x < m) { 26 | t->l = add(t->l, l, m, x, v); 27 | } else { 28 | t->r = add(t->r, m, r, x, v); 29 | } 30 | return t; 31 | } 32 | 33 | int query1(Node *t1, Node *t2, int l, int r, int k) { 34 | if (r - l == 1) { 35 | return l; 36 | } 37 | int m = (l + r) / 2; 38 | int odd = (t1 && t1->r ? t1->r->sumodd : 0) - (t2 && t2->r ? t2->r->sumodd : 0); 39 | int cnt = (t1 && t1->r ? t1->r->sum : 0) - (t2 && t2->r ? t2->r->sum : 0); 40 | if (odd > 0 || cnt > k) { 41 | return query1(t1 ? t1->r : t1, t2 ? t2->r : t2, m, r, k); 42 | } else { 43 | return query1(t1 ? t1->l : t1, t2 ? t2->l : t2, l, m, k - cnt); 44 | } 45 | } 46 | 47 | std::array query2(Node *t1, Node *t2, int l, int r, int k) { 48 | if (r - l == 1) { 49 | int cnt = (t1 ? t1->sumodd : 0) - (t2 ? t2->sumodd : 0); 50 | return {l, cnt, k}; 51 | } 52 | int m = (l + r) / 2; 53 | int cnt = (t1 && t1->r ? t1->r->sumodd : 0) - (t2 && t2->r ? t2->r->sumodd : 0); 54 | if (cnt > k) { 55 | return query2(t1 ? t1->r : t1, t2 ? t2->r : t2, m, r, k); 56 | } else { 57 | return query2(t1 ? t1->l : t1, t2 ? t2->l : t2, l, m, k - cnt); 58 | } 59 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/04 - 数据结构/08B - 其他平衡树.cpp: -------------------------------------------------------------------------------- 1 | /** 其他平衡树 2 | * 2023-08-26: https://codeforces.com/contest/1864/submission/220558951 3 | **/ 4 | struct Node { 5 | Node *l = nullptr; 6 | Node *r = nullptr; 7 | int cnt = 0; 8 | }; 9 | 10 | Node *add(Node *t, int l, int r, int x) { 11 | if (t) { 12 | t = new Node(*t); 13 | } else { 14 | t = new Node; 15 | } 16 | t->cnt += 1; 17 | if (r - l == 1) { 18 | return t; 19 | } 20 | int m = (l + r) / 2; 21 | if (x < m) { 22 | t->l = add(t->l, l, m, x); 23 | } else { 24 | t->r = add(t->r, m, r, x); 25 | } 26 | return t; 27 | } 28 | 29 | int query(Node *t1, Node *t2, int l, int r, int x) { 30 | int cnt = (t2 ? t2->cnt : 0) - (t1 ? t1->cnt : 0); 31 | if (cnt == 0 || l >= x) { 32 | return -1; 33 | } 34 | if (r - l == 1) { 35 | return l; 36 | } 37 | int m = (l + r) / 2; 38 | int res = query(t1 ? t1->r : t1, t2 ? t2->r : t2, m, r, x); 39 | if (res == -1) { 40 | res = query(t1 ? t1->l : t1, t2 ? t2->l : t2, l, m, x); 41 | } 42 | return res; 43 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/04 - 数据结构/08C - 其他平衡树.cpp: -------------------------------------------------------------------------------- 1 | /** 其他平衡树 2 | * 2023-04-03: https://codeforces.com/contest/38/submission/200537139 3 | **/ 4 | struct Info { 5 | int imp = 0; 6 | int id = 0; 7 | }; 8 | 9 | Info operator+(Info a, Info b) { 10 | return {std::max(a.imp, b.imp), 0}; 11 | } 12 | 13 | struct Node { 14 | int w = rng(); 15 | Info info; 16 | Info sum; 17 | int siz = 1; 18 | Node *l = nullptr; 19 | Node *r = nullptr; 20 | }; 21 | 22 | void pull(Node *t) { 23 | t->sum = t->info; 24 | t->siz = 1; 25 | if (t->l) { 26 | t->sum = t->l->sum + t->sum; 27 | t->siz += t->l->siz; 28 | } 29 | if (t->r) { 30 | t->sum = t->sum + t->r->sum; 31 | t->siz += t->r->siz; 32 | } 33 | } 34 | 35 | std::pair splitAt(Node *t, int p) { 36 | if (!t) { 37 | return {t, t}; 38 | } 39 | if (p <= (t->l ? t->l->siz : 0)) { 40 | auto [l, r] = splitAt(t->l, p); 41 | t->l = r; 42 | pull(t); 43 | return {l, t}; 44 | } else { 45 | auto [l, r] = splitAt(t->r, p - 1 - (t->l ? t->l->siz : 0)); 46 | t->r = l; 47 | pull(t); 48 | return {t, r}; 49 | } 50 | } 51 | 52 | void insertAt(Node *&t, int p, Node *x) { 53 | if (!t) { 54 | t = x; 55 | return; 56 | } 57 | if (x->w < t->w) { 58 | auto [l, r] = splitAt(t, p); 59 | t = x; 60 | t->l = l; 61 | t->r = r; 62 | pull(t); 63 | return; 64 | } 65 | if (p <= (t->l ? t->l->siz : 0)) { 66 | insertAt(t->l, p, x); 67 | } else { 68 | insertAt(t->r, p - 1 - (t->l ? t->l->siz : 0), x); 69 | } 70 | pull(t); 71 | } 72 | 73 | Node *merge(Node *a, Node *b) { 74 | if (!a) { 75 | return b; 76 | } 77 | if (!b) { 78 | return a; 79 | } 80 | 81 | if (a->w < b->w) { 82 | a->r = merge(a->r, b); 83 | pull(a); 84 | return a; 85 | } else { 86 | b->l = merge(a, b->l); 87 | pull(b); 88 | return b; 89 | } 90 | } 91 | 92 | int query(Node *t, int v) { 93 | if (!t) { 94 | return 0; 95 | } 96 | if (t->sum.imp < v) { 97 | return t->siz; 98 | } 99 | int res = query(t->r, v); 100 | if (res != (t->r ? t->r->siz : 0)) { 101 | return res; 102 | } 103 | if (t->info.imp > v) { 104 | return res; 105 | } 106 | return res + 1 + query(t->l, v); 107 | } 108 | 109 | void dfs(Node *t) { 110 | if (!t) { 111 | return; 112 | } 113 | dfs(t->l); 114 | std::cout << t->info.id << " "; 115 | dfs(t->r); 116 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/04 - 数据结构/08D - 其他平衡树.cpp: -------------------------------------------------------------------------------- 1 | /** 其他平衡树 2 | * 2023-07-31: https://ac.nowcoder.com/acm/contest/view-submission?submissionId=63162242 3 | **/ 4 | struct Node { 5 | Node *l = nullptr; 6 | Node *r = nullptr; 7 | int cnt = 0; 8 | int cntnew = 0; 9 | }; 10 | 11 | Node *add(int l, int r, int x, int isnew) { 12 | Node *t = new Node; 13 | t->cnt = 1; 14 | t->cntnew = isnew; 15 | if (r - l == 1) { 16 | return t; 17 | } 18 | int m = (l + r) / 2; 19 | if (x < m) { 20 | t->l = add(l, m, x, isnew); 21 | } else { 22 | t->r = add(m, r, x, isnew); 23 | } 24 | return t; 25 | } 26 | 27 | struct Info { 28 | Node *t = nullptr; 29 | int psum = 0; 30 | bool rev = false; 31 | }; 32 | 33 | void pull(Node *t) { 34 | t->cnt = (t->l ? t->l->cnt : 0) + (t->r ? t->r->cnt : 0); 35 | t->cntnew = (t->l ? t->l->cntnew : 0) + (t->r ? t->r->cntnew : 0); 36 | } 37 | 38 | std::pair split(Node *t, int l, int r, int x, bool rev) { 39 | if (!t) { 40 | return {t, t}; 41 | } 42 | if (x == 0) { 43 | return {nullptr, t}; 44 | } 45 | if (x == t->cnt) { 46 | return {t, nullptr}; 47 | } 48 | if (r - l == 1) { 49 | Node *t2 = new Node; 50 | t2->cnt = t->cnt - x; 51 | t->cnt = x; 52 | return {t, t2}; 53 | } 54 | Node *t2 = new Node; 55 | int m = (l + r) / 2; 56 | if (!rev) { 57 | if (t->l && x <= t->l->cnt) { 58 | std::tie(t->l, t2->l) = split(t->l, l, m, x, rev); 59 | t2->r = t->r; 60 | t->r = nullptr; 61 | } else { 62 | std::tie(t->r, t2->r) = split(t->r, m, r, x - (t->l ? t->l->cnt : 0), rev); 63 | } 64 | } else { 65 | if (t->r && x <= t->r->cnt) { 66 | std::tie(t->r, t2->r) = split(t->r, m, r, x, rev); 67 | t2->l = t->l; 68 | t->l = nullptr; 69 | } else { 70 | std::tie(t->l, t2->l) = split(t->l, l, m, x - (t->r ? t->r->cnt : 0), rev); 71 | } 72 | } 73 | pull(t); 74 | pull(t2); 75 | return {t, t2}; 76 | } 77 | 78 | Node *merge(Node *t1, Node *t2, int l, int r) { 79 | if (!t1) { 80 | return t2; 81 | } 82 | if (!t2) { 83 | return t1; 84 | } 85 | if (r - l == 1) { 86 | t1->cnt += t2->cnt; 87 | t1->cntnew += t2->cntnew; 88 | delete t2; 89 | return t1; 90 | } 91 | int m = (l + r) / 2; 92 | t1->l = merge(t1->l, t2->l, l, m); 93 | t1->r = merge(t1->r, t2->r, m, r); 94 | delete t2; 95 | pull(t1); 96 | return t1; 97 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/04 - 数据结构/09 - 分数四则运算(Frac).cpp: -------------------------------------------------------------------------------- 1 | /** 分数四则运算(Frac) 2 | * 2024-07-30: https://qoj.ac/submission/498911 3 | **/ 4 | template 5 | struct Frac { 6 | T num; 7 | T den; 8 | Frac(T num_, T den_) : num(num_), den(den_) { 9 | if (den < 0) { 10 | den = -den; 11 | num = -num; 12 | } 13 | } 14 | Frac() : Frac(0, 1) {} 15 | Frac(T num_) : Frac(num_, 1) {} 16 | explicit operator double() const { 17 | return 1. * num / den; 18 | } 19 | Frac &operator+=(const Frac &rhs) { 20 | num = num * rhs.den + rhs.num * den; 21 | den *= rhs.den; 22 | return *this; 23 | } 24 | Frac &operator-=(const Frac &rhs) { 25 | num = num * rhs.den - rhs.num * den; 26 | den *= rhs.den; 27 | return *this; 28 | } 29 | Frac &operator*=(const Frac &rhs) { 30 | num *= rhs.num; 31 | den *= rhs.den; 32 | return *this; 33 | } 34 | Frac &operator/=(const Frac &rhs) { 35 | num *= rhs.den; 36 | den *= rhs.num; 37 | if (den < 0) { 38 | num = -num; 39 | den = -den; 40 | } 41 | return *this; 42 | } 43 | friend Frac operator+(Frac lhs, const Frac &rhs) { 44 | return lhs += rhs; 45 | } 46 | friend Frac operator-(Frac lhs, const Frac &rhs) { 47 | return lhs -= rhs; 48 | } 49 | friend Frac operator*(Frac lhs, const Frac &rhs) { 50 | return lhs *= rhs; 51 | } 52 | friend Frac operator/(Frac lhs, const Frac &rhs) { 53 | return lhs /= rhs; 54 | } 55 | friend Frac operator-(const Frac &a) { 56 | return Frac(-a.num, a.den); 57 | } 58 | friend bool operator==(const Frac &lhs, const Frac &rhs) { 59 | return lhs.num * rhs.den == rhs.num * lhs.den; 60 | } 61 | friend bool operator!=(const Frac &lhs, const Frac &rhs) { 62 | return lhs.num * rhs.den != rhs.num * lhs.den; 63 | } 64 | friend bool operator<(const Frac &lhs, const Frac &rhs) { 65 | return lhs.num * rhs.den < rhs.num * lhs.den; 66 | } 67 | friend bool operator>(const Frac &lhs, const Frac &rhs) { 68 | return lhs.num * rhs.den > rhs.num * lhs.den; 69 | } 70 | friend bool operator<=(const Frac &lhs, const Frac &rhs) { 71 | return lhs.num * rhs.den <= rhs.num * lhs.den; 72 | } 73 | friend bool operator>=(const Frac &lhs, const Frac &rhs) { 74 | return lhs.num * rhs.den >= rhs.num * lhs.den; 75 | } 76 | friend std::ostream &operator<<(std::ostream &os, Frac x) { 77 | T g = std::gcd(x.num, x.den); 78 | if (x.den == g) { 79 | return os << x.num / g; 80 | } else { 81 | return os << x.num / g << "/" << x.den / g; 82 | } 83 | } 84 | }; 85 | 86 | -------------------------------------------------------------------------------- /03 - jiangly模板收集/04 - 数据结构/10 - 线性基(Basis).cpp: -------------------------------------------------------------------------------- 1 | /** 线性基(Basis) 2 | * 2023-12-03: https://codeforces.com/contest/1902/submission/235594491 3 | **/ 4 | struct Basis { 5 | int a[20] {}; 6 | int t[20] {}; 7 | 8 | Basis() { 9 | std::fill(t, t + 20, -1); 10 | } 11 | 12 | void add(int x, int y = 1E9) { 13 | for (int i = 0; i < 20; i++) { 14 | if (x >> i & 1) { 15 | if (y > t[i]) { 16 | std::swap(a[i], x); 17 | std::swap(t[i], y); 18 | } 19 | x ^= a[i]; 20 | } 21 | } 22 | } 23 | 24 | bool query(int x, int y = 0) { 25 | for (int i = 0; i < 20; i++) { 26 | if ((x >> i & 1) && t[i] >= y) { 27 | x ^= a[i]; 28 | } 29 | } 30 | return x == 0; 31 | } 32 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/05 - 字符串/01A - 马拉车(Manacher).cpp: -------------------------------------------------------------------------------- 1 | /** 马拉车(Manacher, with string) 2 | * 2024-04-06: https://qoj.ac/submission/380047 3 | * 2024-04-09: https://qoj.ac/submission/384389 【模板】 4 | **/ 5 | std::vector manacher(std::string s) { 6 | std::string t = "#"; 7 | for (auto c : s) { 8 | t += c; 9 | t += '#'; 10 | } 11 | int n = t.size(); 12 | std::vector r(n); 13 | for (int i = 0, j = 0; i < n; i++) { 14 | if (2 * j - i >= 0 && j + r[j] > i) { 15 | r[i] = std::min(r[2 * j - i], j + r[j] - i); 16 | } 17 | while (i - r[i] >= 0 && i + r[i] < n && t[i - r[i]] == t[i + r[i]]) { 18 | r[i] += 1; 19 | } 20 | if (i + r[i] > j + r[j]) { 21 | j = i; 22 | } 23 | } 24 | return r; 25 | } 26 | 27 | /** 马拉车(Manacher, with vector) 28 | * 2024-04-14: https://atcoder.jp/contests/abc349/submissions/52365777 29 | **/ 30 | std::vector manacher(std::vector s) { 31 | std::vector t{0}; 32 | for (auto c : s) { 33 | t.push_back(c); 34 | t.push_back(0); 35 | } 36 | int n = t.size(); 37 | std::vector r(n); 38 | for (int i = 0, j = 0; i < n; i++) { 39 | if (2 * j - i >= 0 && j + r[j] > i) { 40 | r[i] = std::min(r[2 * j - i], j + r[j] - i); 41 | } 42 | while (i - r[i] >= 0 && i + r[i] < n && t[i - r[i]] == t[i + r[i]]) { 43 | r[i] += 1; 44 | } 45 | if (i + r[i] > j + r[j]) { 46 | j = i; 47 | } 48 | } 49 | return r; 50 | } 51 | -------------------------------------------------------------------------------- /03 - jiangly模板收集/05 - 字符串/02 - Z函数.cpp: -------------------------------------------------------------------------------- 1 | /** Z函数 2 | * 2023-08-11: https://ac.nowcoder.com/acm/contest/view-submission?submissionId=63378373 3 | * 2024-04-09: https://qoj.ac/submission/384405 【模板】 4 | **/ 5 | std::vector Z(std::string s) { 6 | int n = s.size(); 7 | std::vector z(n + 1); 8 | z[0] = n; 9 | for (int i = 1, j = 1; i < n; i++) { 10 | z[i] = std::max(0, std::min(j + z[j] - i, z[i - j])); 11 | while (i + z[i] < n && s[z[i]] == s[i + z[i]]) { 12 | z[i]++; 13 | } 14 | if (i + z[i] > j + z[j]) { 15 | j = i; 16 | } 17 | } 18 | return z; 19 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/05 - 字符串/03A - 后缀数组(SuffixArray).cpp: -------------------------------------------------------------------------------- 1 | /** 后缀数组(SuffixArray) 2 | * 2023-03-14: https://atcoder.jp/contests/discovery2016-qual/submissions/39727257 3 | * 2023-09-05: https://qoj.ac/submission/164483 4 | * 2024-04-09: https://qoj.ac/submission/384415 【模板】 5 | * 2024-10-18: https://qoj.ac/submission/649558 6 | **/ 7 | struct SuffixArray { 8 | int n; 9 | std::vector sa, rk, lc; 10 | SuffixArray(const std::string &s) { 11 | n = s.length(); 12 | sa.resize(n); 13 | lc.resize(n - 1); 14 | rk.resize(n); 15 | std::iota(sa.begin(), sa.end(), 0); 16 | std::sort(sa.begin(), sa.end(), 17 | [&](int a, int b) { 18 | return s[a] < s[b]; 19 | }); 20 | rk[sa[0]] = 0; 21 | for (int i = 1; i < n; i++) { 22 | rk[sa[i]] = rk[sa[i - 1]] + (s[sa[i]] != s[sa[i - 1]]); 23 | } 24 | int k = 1; 25 | std::vector tmp, cnt(n); 26 | tmp.reserve(n); 27 | while (rk[sa[n - 1]] < n - 1) { 28 | tmp.clear(); 29 | for (int i = 0; i < k; i++) { 30 | tmp.push_back(n - k + i); 31 | } 32 | for (auto i : sa) { 33 | if (i >= k) { 34 | tmp.push_back(i - k); 35 | } 36 | } 37 | std::fill(cnt.begin(), cnt.end(), 0); 38 | for (int i = 0; i < n; i++) { 39 | cnt[rk[i]]++; 40 | } 41 | for (int i = 1; i < n; i++) { 42 | cnt[i] += cnt[i - 1]; 43 | } 44 | for (int i = n - 1; i >= 0; i--) { 45 | sa[--cnt[rk[tmp[i]]]] = tmp[i]; 46 | } 47 | std::swap(rk, tmp); 48 | rk[sa[0]] = 0; 49 | for (int i = 1; i < n; i++) { 50 | rk[sa[i]] = rk[sa[i - 1]] + (tmp[sa[i - 1]] < tmp[sa[i]] || sa[i - 1] + k == n || tmp[sa[i - 1] + k] < tmp[sa[i] + k]); 51 | } 52 | k *= 2; 53 | } 54 | for (int i = 0, j = 0; i < n; i++) { 55 | if (rk[i] == 0) { 56 | j = 0; 57 | } else { 58 | for (j -= (j > 0); i + j < n && sa[rk[i] - 1] + j < n && s[i + j] == s[sa[rk[i] - 1] + j]; j++) 59 | ; 60 | lc[rk[i] - 1] = j; 61 | } 62 | } 63 | } 64 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/05 - 字符串/03B - 后缀数组(SA).cpp: -------------------------------------------------------------------------------- 1 | /** 后缀数组(SA) 2 | * 2023-09-24: https://qoj.ac/submission/187270 3 | * 2024-04-07: https://qoj.ac/submission/381482 4 | **/ 5 | struct SA { 6 | int n; 7 | std::vector sa, rk, lc; 8 | 9 | SA(std::string s) { 10 | n = s.size(); 11 | sa.resize(n); 12 | lc.resize(n - 1); 13 | rk.resize(n); 14 | std::iota(sa.begin(), sa.end(), 0); 15 | std::sort(sa.begin(), sa.end(), 16 | [&](int a, int b) { 17 | return s[a] < s[b]; 18 | }); 19 | rk[sa[0]] = 0; 20 | for (int i = 1; i < n; i++) { 21 | rk[sa[i]] = rk[sa[i - 1]] + (s[sa[i]] != s[sa[i - 1]]); 22 | } 23 | int k = 1; 24 | std::vector tmp, cnt(n); 25 | tmp.reserve(n); 26 | while (rk[sa[n - 1]] < n - 1) { 27 | tmp.clear(); 28 | for (int i = 0; i < k; i++) { 29 | tmp.push_back(n - k + i); 30 | } 31 | for (auto i : sa) { 32 | if (i >= k) { 33 | tmp.push_back(i - k); 34 | } 35 | } 36 | std::fill(cnt.begin(), cnt.end(), 0); 37 | for (int i = 0; i < n; i++) { 38 | cnt[rk[i]]++; 39 | } 40 | for (int i = 1; i < n; i++) { 41 | cnt[i] += cnt[i - 1]; 42 | } 43 | for (int i = n - 1; i >= 0; i--) { 44 | sa[--cnt[rk[tmp[i]]]] = tmp[i]; 45 | } 46 | std::swap(rk, tmp); 47 | rk[sa[0]] = 0; 48 | for (int i = 1; i < n; i++) { 49 | rk[sa[i]] = rk[sa[i - 1]] + (tmp[sa[i - 1]] < tmp[sa[i]] || sa[i - 1] + k == n || tmp[sa[i - 1] + k] < tmp[sa[i] + k]); 50 | } 51 | k *= 2; 52 | } 53 | for (int i = 0, j = 0; i < n; i++) { 54 | if (rk[i] == 0) { 55 | j = 0; 56 | } else { 57 | for (j -= j > 0; i + j < n && sa[rk[i] - 1] + j < n && s[i + j] == s[sa[rk[i] - 1] + j]; ) { 58 | j++; 59 | } 60 | lc[rk[i] - 1] = j; 61 | } 62 | } 63 | } 64 | }; 65 | 66 | void solve() { 67 | constexpr int K = 21; 68 | std::vector st(K, std::vector(l - 1)); 69 | st[0] = lc; 70 | for (int j = 0; j < K - 1; j++) { 71 | for (int i = 0; i + (2 << j) <= l - 1; i++) { 72 | st[j + 1][i] = std::min(st[j][i], st[j][i + (1 << j)]); 73 | } 74 | } 75 | 76 | auto rmq = [&](int l, int r) { 77 | int k = std::__lg(r - l); 78 | return std::min(st[k][l], st[k][r - (1 << k)]); 79 | }; 80 | 81 | auto lcp = [&](int i, int j) { 82 | if (i == j || i == n || j == n) { 83 | return std::min(n - i, n - j); 84 | } 85 | int a = rk[i]; 86 | int b = rk[j]; 87 | if (a > b) { 88 | std::swap(a, b); 89 | } 90 | return std::min({n - i, n - j, rmq(a, b)}); 91 | }; 92 | 93 | auto lcs = [&](int i, int j) { 94 | if (i == j || i == 0 || j == 0) { 95 | return std::min(i, j); 96 | } 97 | int a = rk[n + n - i]; 98 | int b = rk[n + n - j]; 99 | if (a > b) { 100 | std::swap(a, b); 101 | } 102 | return std::min({i, j, rmq(a, b)}); 103 | }; 104 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/05 - 字符串/04A - 后缀自动机(SuffixAutomaton 旧版).cpp: -------------------------------------------------------------------------------- 1 | /** 后缀自动机(SuffixAutomaton 旧版) 2 | * 2022-08-17: https://ac.nowcoder.com/acm/contest/view-submission?submissionId=53409023&returnHomeType=1&uid=329687984 3 | **/ 4 | struct SuffixAutomaton { 5 | static constexpr int ALPHABET_SIZE = 26, N = 5e5; 6 | struct Node { 7 | int len; 8 | int link; 9 | int next[ALPHABET_SIZE]; 10 | Node() : len(0), link(0), next{} {} 11 | } t[2 * N]; 12 | int cntNodes; 13 | SuffixAutomaton() { 14 | cntNodes = 1; 15 | std::fill(t[0].next, t[0].next + ALPHABET_SIZE, 1); 16 | t[0].len = -1; 17 | } 18 | int extend(int p, int c) { 19 | if (t[p].next[c]) { 20 | int q = t[p].next[c]; 21 | if (t[q].len == t[p].len + 1) 22 | return q; 23 | int r = ++cntNodes; 24 | t[r].len = t[p].len + 1; 25 | t[r].link = t[q].link; 26 | std::copy(t[q].next, t[q].next + ALPHABET_SIZE, t[r].next); 27 | t[q].link = r; 28 | while (t[p].next[c] == q) { 29 | t[p].next[c] = r; 30 | p = t[p].link; 31 | } 32 | return r; 33 | } 34 | int cur = ++cntNodes; 35 | t[cur].len = t[p].len + 1; 36 | while (!t[p].next[c]) { 37 | t[p].next[c] = cur; 38 | p = t[p].link; 39 | } 40 | t[cur].link = extend(p, c); 41 | return cur; 42 | } 43 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/05 - 字符串/04B - 后缀自动机(SAM 新版).cpp: -------------------------------------------------------------------------------- 1 | /** 后缀自动机(SAM 新版) 2 | * 2023-05-27: https://cf.dianhsu.com/gym/104353/submission/207318083 3 | * 2023-09-25: https://qoj.ac/submission/188106 4 | * 2024-04-09: https://qoj.ac/submission/384423 【模板】 5 | * 2024-04-09: https://qoj.ac/submission/384429 【模板】 6 | **/ 7 | struct SAM { 8 | static constexpr int ALPHABET_SIZE = 26; 9 | struct Node { 10 | int len; 11 | int link; 12 | std::array next; 13 | Node() : len{}, link{}, next{} {} 14 | }; 15 | std::vector t; 16 | SAM() { 17 | init(); 18 | } 19 | void init() { 20 | t.assign(2, Node()); 21 | t[0].next.fill(1); 22 | t[0].len = -1; 23 | } 24 | int newNode() { 25 | t.emplace_back(); 26 | return t.size() - 1; 27 | } 28 | int extend(int p, int c) { 29 | if (t[p].next[c]) { 30 | int q = t[p].next[c]; 31 | if (t[q].len == t[p].len + 1) { 32 | return q; 33 | } 34 | int r = newNode(); 35 | t[r].len = t[p].len + 1; 36 | t[r].link = t[q].link; 37 | t[r].next = t[q].next; 38 | t[q].link = r; 39 | while (t[p].next[c] == q) { 40 | t[p].next[c] = r; 41 | p = t[p].link; 42 | } 43 | return r; 44 | } 45 | int cur = newNode(); 46 | t[cur].len = t[p].len + 1; 47 | while (!t[p].next[c]) { 48 | t[p].next[c] = cur; 49 | p = t[p].link; 50 | } 51 | t[cur].link = extend(p, c); 52 | return cur; 53 | } 54 | int extend(int p, char c, char offset = 'a') { 55 | return extend(p, c - offset); 56 | } 57 | 58 | int next(int p, int x) { 59 | return t[p].next[x]; 60 | } 61 | 62 | int next(int p, char c, char offset = 'a') { 63 | return next(p, c - 'a'); 64 | } 65 | 66 | int link(int p) { 67 | return t[p].link; 68 | } 69 | 70 | int len(int p) { 71 | return t[p].len; 72 | } 73 | 74 | int size() { 75 | return t.size(); 76 | } 77 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/05 - 字符串/05 - 回文自动机(PAM).cpp: -------------------------------------------------------------------------------- 1 | /** 回文自动机(PAM) 2 | * 2023-05-19: https://ac.nowcoder.com/acm/contest/view-submission?submissionId=62237107&returnHomeType=1&uid=329687984 3 | * 2024-04-09: https://qoj.ac/submission/384435 【模板】 4 | **/ 5 | struct PAM { 6 | static constexpr int ALPHABET_SIZE = 26; 7 | struct Node { 8 | int len; 9 | int link; 10 | int cnt; 11 | std::array next; 12 | Node() : len{}, link{}, cnt{}, next{} {} 13 | }; 14 | std::vector t; 15 | int suff; 16 | std::string s; 17 | PAM() { 18 | init(); 19 | } 20 | void init() { 21 | t.assign(2, Node()); 22 | t[0].len = -1; 23 | suff = 1; 24 | s.clear(); 25 | } 26 | int newNode() { 27 | t.emplace_back(); 28 | return t.size() - 1; 29 | } 30 | bool add(char c) { 31 | int pos = s.size(); 32 | s += c; 33 | int let = c - 'a'; 34 | int cur = suff, curlen = 0; 35 | while (true) { 36 | curlen = t[cur].len; 37 | if (pos - 1 - curlen >= 0 && s[pos - 1 - curlen] == s[pos]) { 38 | break; 39 | } 40 | cur = t[cur].link; 41 | } 42 | if (t[cur].next[let]) { 43 | suff = t[cur].next[let]; 44 | return false; 45 | } 46 | int num = newNode(); 47 | suff = num; 48 | t[num].len = t[cur].len + 2; 49 | t[cur].next[let] = num; 50 | if (t[num].len == 1) { 51 | t[num].link = 1; 52 | t[num].cnt = 1; 53 | return true; 54 | } 55 | while (true) { 56 | cur = t[cur].link; 57 | curlen = t[cur].len; 58 | if (pos - 1 - curlen >= 0 && s[pos - 1 - curlen] == s[pos]) { 59 | t[num].link = t[cur].next[let]; 60 | break; 61 | } 62 | } 63 | t[num].cnt = 1 + t[t[num].link].cnt; 64 | return true; 65 | } 66 | int next(int p, int x) { 67 | return t[p].next[x]; 68 | } 69 | int link(int p) { 70 | return t[p].link; 71 | } 72 | int len(int p) { 73 | return t[p].len; 74 | } 75 | int size() { 76 | return t.size(); 77 | } 78 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/05 - 字符串/06A - AC自动机(AC 旧版).cpp: -------------------------------------------------------------------------------- 1 | /** AC自动机(AC 旧版) 2 | * 2021-07-07: https://codeforces.com/contest/710/submission/121661266 3 | **/ 4 | constexpr int N = 3e5 + 30, A = 26; 5 | 6 | struct Node { 7 | int fail; 8 | int sum; 9 | int next[A]; 10 | Node() : fail(-1), sum(0) { 11 | std::memset(next, -1, sizeof(next)); 12 | } 13 | } node[N]; 14 | 15 | int cnt = 0; 16 | int bin[N]; 17 | int nBin = 0; 18 | 19 | int newNode() { 20 | int p = nBin > 0 ? bin[--nBin] : cnt++; 21 | node[p] = Node(); 22 | return p; 23 | } 24 | 25 | struct AC { 26 | std::vector x; 27 | AC(AC &&a) : x(std::move(a.x)) {} 28 | AC(std::vector s, std::vector w) { 29 | x = {newNode(), newNode()}; 30 | std::fill(node[x[0]].next, node[x[0]].next + A, x[1]); 31 | node[x[1]].fail = x[0]; 32 | 33 | for (int i = 0; i < int(s.size()); i++) { 34 | int p = x[1]; 35 | for (int j = 0; j < int(s[i].length()); j++) { 36 | int c = s[i][j] - 'a'; 37 | if (node[p].next[c] == -1) { 38 | int u = newNode(); 39 | x.push_back(u); 40 | node[p].next[c] = u; 41 | } 42 | p = node[p].next[c]; 43 | } 44 | node[p].sum += w[i]; 45 | } 46 | 47 | std::queue que; 48 | que.push(x[1]); 49 | while (!que.empty()) { 50 | int u = que.front(); 51 | que.pop(); 52 | node[u].sum += node[node[u].fail].sum; 53 | for (int c = 0; c < A; c++) { 54 | if (node[u].next[c] == -1) { 55 | node[u].next[c] = node[node[u].fail].next[c]; 56 | } else { 57 | node[node[u].next[c]].fail = node[node[u].fail].next[c]; 58 | que.push(node[u].next[c]); 59 | } 60 | } 61 | } 62 | } 63 | ~AC() { 64 | for (auto p : x) { 65 | bin[nBin++] = p; 66 | } 67 | } 68 | i64 query(const std::string &s) const { 69 | i64 ans = 0; 70 | int p = x[1]; 71 | for (int i = 0; i < int(s.length()); i++) { 72 | int c = s[i] - 'a'; 73 | p = node[p].next[c]; 74 | ans += node[p].sum; 75 | } 76 | return ans; 77 | } 78 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/05 - 字符串/06B - AC自动机(AhoCorasick, with vector 新版).cpp: -------------------------------------------------------------------------------- 1 | /** AC自动机(AhoCorasick, with vector 新版) 2 | * 2023-04-07: https://codeforces.com/contest/1801/submission/201155712 3 | **/ 4 | struct AhoCorasick { 5 | static constexpr int ALPHABET = 26; 6 | struct Node { 7 | int len; 8 | int link; 9 | std::array next; 10 | Node() : link{}, next{} {} 11 | }; 12 | 13 | std::vector t; 14 | 15 | AhoCorasick() { 16 | init(); 17 | } 18 | 19 | void init() { 20 | t.assign(2, Node()); 21 | t[0].next.fill(1); 22 | t[0].len = -1; 23 | } 24 | 25 | int newNode() { 26 | t.emplace_back(); 27 | return t.size() - 1; 28 | } 29 | 30 | int add(const std::vector &a) { 31 | int p = 1; 32 | for (auto x : a) { 33 | if (t[p].next[x] == 0) { 34 | t[p].next[x] = newNode(); 35 | t[t[p].next[x]].len = t[p].len + 1; 36 | } 37 | p = t[p].next[x]; 38 | } 39 | return p; 40 | } 41 | 42 | int add(const std::string &a, char offset = 'a') { 43 | std::vector b(a.size()); 44 | for (int i = 0; i < a.size(); i++) { 45 | b[i] = a[i] - offset; 46 | } 47 | return add(b); 48 | } 49 | 50 | void work() { 51 | std::queue q; 52 | q.push(1); 53 | 54 | while (!q.empty()) { 55 | int x = q.front(); 56 | q.pop(); 57 | 58 | for (int i = 0; i < ALPHABET; i++) { 59 | if (t[x].next[i] == 0) { 60 | t[x].next[i] = t[t[x].link].next[i]; 61 | } else { 62 | t[t[x].next[i]].link = t[t[x].link].next[i]; 63 | q.push(t[x].next[i]); 64 | } 65 | } 66 | } 67 | } 68 | 69 | int next(int p, int x) { 70 | return t[p].next[x]; 71 | } 72 | 73 | int next(int p, char c, char offset = 'a') { 74 | return next(p, c - 'a'); 75 | } 76 | 77 | int link(int p) { 78 | return t[p].link; 79 | } 80 | 81 | int len(int p) { 82 | return t[p].len; 83 | } 84 | 85 | int size() { 86 | return t.size(); 87 | } 88 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/05 - 字符串/06C - AC自动机(AhoCorasick, with string 新版).cpp: -------------------------------------------------------------------------------- 1 | /** AC自动机(AhoCorasick, with string 新版) 2 | * 2024-04-09: https://www.luogu.com.cn/record/155114676 【模板】 3 | **/ 4 | struct AhoCorasick { 5 | static constexpr int ALPHABET = 26; 6 | struct Node { 7 | int len; 8 | int link; 9 | std::array next; 10 | Node() : len{0}, link{0}, next{} {} 11 | }; 12 | 13 | std::vector t; 14 | 15 | AhoCorasick() { 16 | init(); 17 | } 18 | 19 | void init() { 20 | t.assign(2, Node()); 21 | t[0].next.fill(1); 22 | t[0].len = -1; 23 | } 24 | 25 | int newNode() { 26 | t.emplace_back(); 27 | return t.size() - 1; 28 | } 29 | 30 | int add(const std::string &a) { 31 | int p = 1; 32 | for (auto c : a) { 33 | int x = c - 'a'; 34 | if (t[p].next[x] == 0) { 35 | t[p].next[x] = newNode(); 36 | t[t[p].next[x]].len = t[p].len + 1; 37 | } 38 | p = t[p].next[x]; 39 | } 40 | return p; 41 | } 42 | 43 | void work() { 44 | std::queue q; 45 | q.push(1); 46 | 47 | while (!q.empty()) { 48 | int x = q.front(); 49 | q.pop(); 50 | 51 | for (int i = 0; i < ALPHABET; i++) { 52 | if (t[x].next[i] == 0) { 53 | t[x].next[i] = t[t[x].link].next[i]; 54 | } else { 55 | t[t[x].next[i]].link = t[t[x].link].next[i]; 56 | q.push(t[x].next[i]); 57 | } 58 | } 59 | } 60 | } 61 | 62 | int next(int p, int x) { 63 | return t[p].next[x]; 64 | } 65 | 66 | int link(int p) { 67 | return t[p].link; 68 | } 69 | 70 | int len(int p) { 71 | return t[p].len; 72 | } 73 | 74 | int size() { 75 | return t.size(); 76 | } 77 | }; -------------------------------------------------------------------------------- /03 - jiangly模板收集/05 - 字符串/07 - 字符串哈希(随机底模例题).cpp: -------------------------------------------------------------------------------- 1 | /** 字符串哈希(随机底模例题) 2 | * 2022-06-09: https://codeforces.com/contest/1598/submission/160006998 3 | **/ 4 | 5 | #include 6 | 7 | using i64 = long long; 8 | 9 | bool isprime(int n) { 10 | if (n <= 1) { 11 | return false; 12 | } 13 | for (int i = 2; i * i <= n; i++) { 14 | if (n % i == 0) { 15 | return false; 16 | } 17 | } 18 | return true; 19 | } 20 | 21 | int findPrime(int n) { 22 | while (!isprime(n)) { 23 | n++; 24 | } 25 | return n; 26 | } 27 | 28 | using Hash = std::array; 29 | 30 | int main() { 31 | std::ios::sync_with_stdio(false); 32 | std::cin.tie(nullptr); 33 | 34 | std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count()); 35 | 36 | const int P = findPrime(rng() % 900000000 + 100000000); 37 | 38 | std::string s, x; 39 | std::cin >> s >> x; 40 | 41 | int n = s.length(); 42 | int m = x.length(); 43 | 44 | std::vector h(n + 1), p(n + 1); 45 | for (int i = 0; i < n; i++) { 46 | h[i + 1] = (10LL * h[i] + s[i] - '0') % P; 47 | } 48 | p[0] = 1; 49 | for (int i = 0; i < n; i++) { 50 | p[i + 1] = 10LL * p[i] % P; 51 | } 52 | 53 | auto get = [&](int l, int r) { 54 | return (h[r] + 1LL * (P - h[l]) * p[r - l]) % P; 55 | }; 56 | 57 | int px = 0; 58 | for (auto c : x) { 59 | px = (10LL * px + c - '0') % P; 60 | } 61 | 62 | for (int i = 0; i <= n - 2 * (m - 1); i++) { 63 | if ((get(i, i + m - 1) + get(i + m - 1, i + 2 * m - 2)) % P == px) { 64 | std::cout << i + 1 << " " << i + m - 1 << "\n"; 65 | std::cout << i + m << " " << i + 2 * m - 2 << "\n"; 66 | return 0; 67 | } 68 | } 69 | 70 | std::vector z(m + 1), f(n + 1); 71 | z[0] = m; 72 | 73 | for (int i = 1, j = -1; i < m; i++) { 74 | if (j != -1) { 75 | z[i] = std::max(0, std::min(j + z[j] - i, z[i - j])); 76 | } 77 | while (z[i] + i < m && x[z[i]] == x[z[i] + i]) { 78 | z[i]++; 79 | } 80 | if (j == -1 || i + z[i] > j + z[j]) { 81 | j = i; 82 | } 83 | } 84 | for (int i = 0, j = -1; i < n; i++) { 85 | if (j != -1) { 86 | f[i] = std::max(0, std::min(j + f[j] - i, z[i - j])); 87 | } 88 | while (f[i] + i < n && f[i] < m && x[f[i]] == s[f[i] + i]) { 89 | f[i]++; 90 | } 91 | if (j == -1 || i + f[i] > j + f[j]) { 92 | j = i; 93 | } 94 | } 95 | 96 | for (int i = 0; i + m <= n; i++) { 97 | int l = std::min(m, f[i]); 98 | 99 | for (auto j : { m - l, m - l - 1 }) { 100 | if (j <= 0) { 101 | continue; 102 | } 103 | if (j <= i && (get(i - j, i) + get(i, i + m)) % P == px) { 104 | std::cout << i - j + 1 << " " << i << "\n"; 105 | std::cout << i + 1 << " " << i + m << "\n"; 106 | return 0; 107 | } 108 | if (i + m + j <= n && (get(i, i + m) + get(i + m, i + m + j)) % P == px) { 109 | std::cout << i + 1 << " " << i + m << "\n"; 110 | std::cout << i + m + 1 << " " << i + m + j << "\n"; 111 | return 0; 112 | } 113 | } 114 | } 115 | 116 | return 0; 117 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/05 - 字符串/08 - 最长公共前缀 LCP(例题).cpp: -------------------------------------------------------------------------------- 1 | /** 最长公共前缀 LCP(例题) 2 | * 2024-03-02: https://qoj.ac/submission/343378 3 | **/ 4 | constexpr int L = 2E6 + 10; 5 | 6 | int len[L]; 7 | int lnk[L]; 8 | int nxt[L][26]; 9 | 10 | int f[L]; 11 | int tot = 1; 12 | 13 | std::vector adj[L]; 14 | 15 | int extend(int p, int c) { 16 | if (nxt[p][c]) { 17 | int q = nxt[p][c]; 18 | if (len[q] == len[p] + 1) { 19 | return q; 20 | } 21 | int r = ++tot; 22 | len[r] = len[p] + 1; 23 | lnk[r] = lnk[q]; 24 | std::copy(nxt[q], nxt[q] + 26, nxt[r]); 25 | lnk[q] = r; 26 | while (nxt[p][c] == q) { 27 | nxt[p][c] = r; 28 | p = lnk[p]; 29 | } 30 | return r; 31 | } 32 | int cur = ++tot; 33 | len[cur] = len[p] + 1; 34 | while (!nxt[p][c]) { 35 | nxt[p][c] = cur; 36 | p = lnk[p]; 37 | } 38 | lnk[cur] = extend(p, c); 39 | return cur; 40 | } 41 | 42 | int main() { 43 | std::ios::sync_with_stdio(false); 44 | std::cin.tie(nullptr); 45 | 46 | std::fill(nxt[0], nxt[0] + 26, 1); 47 | len[0] = -1; 48 | 49 | int N; 50 | std::cin >> N; 51 | 52 | std::vector S(N); 53 | for (int i = 0; i < N; i++) { 54 | std::cin >> S[i]; 55 | int p = 1; 56 | for (auto c : S[i]) { 57 | p = extend(p, c - 'a'); 58 | if (f[p] != -1) { 59 | if (f[p] == 0) { 60 | f[p] = i + 1; 61 | } else if (f[p] != i + 1) { 62 | f[p] = -1; 63 | } 64 | } 65 | } 66 | } 67 | 68 | for (int i = 1; i <= tot; i++) { 69 | adj[lnk[i]].push_back(i); 70 | } 71 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/05 - 字符串/09B - 字典树 Trie.cpp: -------------------------------------------------------------------------------- 1 | /** 字典树 Trie 2 | * 2024-06-18: https://cf.dianhsu.com/gym/105222/submission/266217560 3 | * 2024-06-04: https://codeforces.com/contest/1980/submission/263960334 4 | **/ 5 | constexpr int N = 1E7; 6 | constexpr int inf = 1E9; 7 | int tot; 8 | int trie[N][2]; 9 | int f[N]; 10 | 11 | int newNode() { 12 | int x = ++tot; 13 | trie[x][0] = trie[x][1] = 0; 14 | f[x] = inf; 15 | return x; 16 | } 17 | void add(int x, int i) { 18 | int p = 1; 19 | for (int j = 29; j >= 0; j--) { 20 | int &q = trie[p][x >> j & 1]; 21 | if (q == 0) { 22 | q = newNode(); 23 | } 24 | p = q; 25 | f[p] = std::min(f[p], i); 26 | } 27 | } 28 | 29 | int query(int a, int b) { 30 | int ans1 = inf, ans2 = inf; 31 | int p = 1; 32 | for (int i = 29; i >= 0; i--) { 33 | int d = a >> i & 1; 34 | int e = b >> i & 1; 35 | if (e) { 36 | ans1 = std::min(ans1, f[trie[p][d]]); 37 | } else { 38 | ans2 = std::min(ans2, f[trie[p][d ^ 1]]); 39 | } 40 | p = trie[p][e ^ d]; 41 | } 42 | ans1 = std::min(ans1, f[p]); 43 | ans2 = std::min(ans2, f[p]); 44 | if (ans1 == inf || ans2 == inf) { 45 | return -1; 46 | } 47 | return std::max({1, ans1, ans2}); 48 | } -------------------------------------------------------------------------------- /03 - jiangly模板收集/05 - 字符串/10 - 前缀函数(KMP).cpp: -------------------------------------------------------------------------------- 1 | /** 前缀函数(KMP) 2 | * 2023-11-17: https://qoj.ac/submission/253754 3 | * 2024-04-09: https://qoj.ac/submission/384408 【模板】 4 | **/ 5 | std::vector kmp(std::string s) { 6 | int n = s.size(); 7 | std::vector f(n + 1); 8 | for (int i = 1, j = 0; i < n; i++) { 9 | while (j && s[i] != s[j]) { 10 | j = f[j]; 11 | } 12 | j += (s[i] == s[j]); 13 | f[i + 1] = j; 14 | } 15 | return f; 16 | } -------------------------------------------------------------------------------- /04 - 历年XCPC赛事补题链接整理/README.md: -------------------------------------------------------------------------------- 1 | [TOC] 2 | 3 | # 赛制安排 4 | 5 | 1. 各地省赛:一般而言,各地每一年都会举办本省的赛事;为了增加影响力,部分省份会与 ICPC 或 CCPC 的主办方合作举办,将部分正式赛外卡名额作为激励名额,奖励给省赛排名靠前的学校,通常这样的省赛会标注“暨ICPC/CCPC邀请赛”,且会位于上半年举行。**由于关系较为暧昧,有时难以区分,本表可能会出现偏差,如有可靠信息,可以直接挂一条 Issues**。 6 | 2. 区域赛:目前公认的最大的赛事为 ICPC 与 CCPC 两大赛事。其余还有一些蓝桥杯、天梯赛,含金量没这么高。 7 | 3. 区域赛预选赛:疫情后这些年,会在暑期举办全国网络预选赛(ICPC办两场合并名次、CCPC办一场),作为主要参赛名额发放手段,而上半年举行的邀请赛作为次要参赛名额发放手段。在获得名额后,由学校内部分配队伍,自由选择参加去哪一场区域赛(但每场都会有最大队伍数量限制、每个队伍每年至多参加两场不同的区域赛);在疫情前则每个区域赛都有自己的预选赛,具体规则不明。 8 | 4. 区域赛正赛:一般会在每年下半年举行,举办地不定,场次不定,但一般会在固定的合作高校中轮流举办;除此之外,港澳赛区每年固定一场,台湾赛区暂不归属于cn赛区。 9 | 10 | # 规约 11 | 12 | 1. 首发于[个人博客](https://www.cnblogs.com/WIDA/p/18122995),同步更新于[GitHub](https://github.com/hh2048/xcpc)。没在表上的、整理有误的欢迎找我交流。除此之外,限于精力本表只整理了域内的比赛,[QOJ](https://qoj.ac/category/1)有世界各区域比赛整理页面。 13 | 2. 所有 QOJ、UCUP 的比赛可能存在改题、换题序的问题;所有计蒜客的比赛可能存在无法补题的现象,请大家自行判断。 14 | 3. 全部邀请赛都归类在省赛列表中,不再单独计算。 15 | 4. 存在别名的赛事会使用斜体标注(一般多为 Universal Cup 的二次命名),但不确保收集全面。 16 | 5. 按举办时间从早到晚排序,之后可能会升级为 Excel 方便查询,还没确定技术路线。 17 | 6. 官方名称上的蓝链为 [board](https://board.xcpcio.com/) 的外榜排名。 18 | 7. 目前只整理到2018年,更早的可以灌注QQ群730602769谢谢喵。 19 | 20 | # 更多相关资料 21 | 22 | ## 官方 23 | 24 | 1. [ACM-ICPC 2017 Asia Official Regional Rules](https://blog.sina.com.cn/s/blog_b946da100102xmw1.html) 黄金雄博士博客上的官方规则(2017) 25 | 2. [ACM-ICPC 中国赛区2013 特别规则](https://blog.sina.com.cn/s/blog_b946da100101jh55.html) 黄金雄博士博客上的官方规则(2013) 26 | 27 | ## 引用与交叉验证 28 | 29 | 30 | 1. [ICPC](https://icpc.global/regionals/rules) 2024/25区域赛规则 31 | 1. [ACM/ICPC 信息站](http://acmicpc.info/) 收录了2021年疫情前的大量赛事信息,提供交叉验证 32 | 1. [ProtectEMmm](https://vjudge.net/article/3389)在 VJ 上对于2022赛季ICPC/CCPC赛事的整理 33 | 2. [ProtectEMmm](https://vjudge.net/article/3893)在 VJ 上对于2021赛季ICPC/CCPC赛事的整理 34 | 3. [qq1735705660](https://vjudge.net/article/2446)在 VJ 上对于2019赛季ICPC/CCPC赛事的整理 35 | 4. [hzu1514080902215](https://vjudge.net/article/836)在 VJ 上对于2018赛季ICPC赛事的整理 36 | -------------------------------------------------------------------------------- /05 - 算法竞赛命题白皮书/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hh2048/XCPC/5d09845515bfdb7d2e9e19badd3c5e351f6da2af/05 - 算法竞赛命题白皮书/README.md -------------------------------------------------------------------------------- /06 - 个人博客搬运/算法笔记/05 - LLVM(代码自动格式化)探究.md: -------------------------------------------------------------------------------- 1 | ### 汇总 2 | 3 | 本章节部分内容参考自[网络文章1](https://blog.csdn.net/weixin_43717839/article/details/129382657)、[网络文章2](https://blog.csdn.net/sirkang/article/details/105988953)。 4 | 5 | LLVM是一个代码格式化工具,修改一些默认参数使之更好的符合你的代码风格,以下是我个人的调整内容。 6 | 7 | ```LLVM 8 | BasedOnStyle: LLVM 9 | IndentWidth: 4 10 | 11 | AllowShortIfStatementsOnASingleLine: true 12 | AllowShortLoopsOnASingleLine: true 13 | AllowShortFunctionsOnASingleLine: Empty 14 | 15 | NamespaceIndentation: All 16 | ColumnLimit: 70 17 | IndentPPDirectives: BeforeHash 18 | SpaceAfterTemplateKeyword: false 19 | ReflowComments: true 20 | AlignTrailingComments: false 21 | MaxEmptyLinesToKeep: 2 22 | ``` 23 | 24 | *** 25 | 26 | ### 参数调节细节 27 | 28 | `AllowShortLoopsOnASingleLine: true` 允许合并短的循环到单行上:可以让**没有括号**单独一行的短语句合并到上一行的结尾。 29 | 30 | ```c++ 31 | // 格式化前 32 | void solve() { 33 | while (1) 34 | continue; 35 | } 36 | 37 | // 格式化后 38 | void solve() { 39 | while (1) continue; 40 | } 41 | ``` 42 | 43 | `AllowShortIfStatementsOnASingleLine : true` 允许合并短的if语句到单行上:同上,注意带 `else` 或 `else if` 后不生效。 44 | 45 | `AllowShortFunctionsOnASingleLine: Empty` 仅允许合并短的空函数到单行上。 46 | 47 | `NamespaceIndentation : All` 缩进全部的命名空间。 48 | 49 | `ColumnLimit : 100` 列数限制,列数超过这个值的行会被自动换行。 50 | 51 | `IndentPPDirectives : BeforeHash` 对 `define` 语句缩进。 52 | 53 | ```c++ 54 | // 设置为None时 55 | #ifndef ONLINE_JUDGE 56 | #define tout cout 57 | #include 58 | #endif 59 | 60 | // 设置为AfterHash时 61 | #ifndef ONLINE_JUDGE 62 | # define tout cout 63 | # include 64 | #endif 65 | 66 | // 设置为BeforeHash时 67 | #ifndef ONLINE_JUDGE 68 | #define tout cout 69 | #include 70 | #endif 71 | ``` 72 | 73 | `SpaceAfterTemplateKeyword: false` 不在可变模板关键字 `template` 后插入空格。 74 | 75 | ```c++ 76 | // 不设置时 77 | template void chk(T x) {} 78 | 79 | // 设置为false时 80 | template void chk(T x) {} 81 | ``` 82 | 83 | `MaxEmptyLinesToKeep: 2` 设置最大持续空行:如果有非常多空行,会被自动删除到剩两行。 84 | 85 | `ReflowComments : true` 会对列数超过 `ColumnLimit` 设置的注释进行自动换行。 86 | 87 | ```c++ 88 | // 格式化前 89 | // veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information 90 | /* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information */ 91 | 92 | // 格式化后 93 | // veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of 94 | // information 95 | /* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of 96 | * information */ 97 | ``` 98 | 99 | `AlignTrailingComments : false` 不对齐尾部注释。 100 | 101 | ```c++ 102 | // 设置为true时 103 | int a = 0; // 初始化a 104 | int cnt = 20; // 初始化cnt 105 | 106 | // 设置为false时 107 | int a = 0; // 初始化a 108 | int cnt = 20; // 初始化cnt 109 | ``` -------------------------------------------------------------------------------- /06 - 个人博客搬运/算法笔记/06 - p01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hh2048/XCPC/5d09845515bfdb7d2e9e19badd3c5e351f6da2af/06 - 个人博客搬运/算法笔记/06 - p01.png -------------------------------------------------------------------------------- /06 - 个人博客搬运/算法笔记/06 - p02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hh2048/XCPC/5d09845515bfdb7d2e9e19badd3c5e351f6da2af/06 - 个人博客搬运/算法笔记/06 - p02.png -------------------------------------------------------------------------------- /06 - 个人博客搬运/算法笔记/06 - p03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hh2048/XCPC/5d09845515bfdb7d2e9e19badd3c5e351f6da2af/06 - 个人博客搬运/算法笔记/06 - p03.png -------------------------------------------------------------------------------- /06 - 个人博客搬运/算法笔记/06 - 本地编译器编译设置.md: -------------------------------------------------------------------------------- 1 | ### 编译命令相关 2 | 3 | 以下是我正在使用的完整编译命令: 4 | 5 | ```c++ 6 | g++ -O2 -std=c++20 -pipe 7 | -Wall -Wextra -Wconversion 8 | -fstack-protector 9 | -Wl,--stack=268435456 10 | ``` 11 | 12 | 下面是每个选项的解释: 13 | 14 | 1. **-std=c++20**:使用C++20标准进行编译。这告诉编译器使用C++20版本的语法和特性进行编译。C++20是C++的最新标准,引入了许多新的特性和改进。 15 | 16 | 2. **-O2**:启用 `O2` 级别的优化(一般戏称为氧气优化)。这个选项告诉编译器进行中等程度的优化,以提高程序的运行速度。 17 | 18 | 3. **-pipe**:与 `-O2` 类似,进行一定程度的优化提速。 19 | 20 | 4. **-Wall**:启用所有常规的警告信息。这会让编译器提醒你可能会导致问题的代码部分。这是一个推荐的编译选项,因为它帮助你捕捉潜在的错误。 21 | 22 | 5. **-Wextra**:启用额外的警告。类似于 `-Wall`,但会启用更多的警告,包括一些非常规的警告,帮助你更好地检查代码。 23 | 24 | 6. **-Wconversion**:启用类型转换相关的警告。这个选项会在可能导致数据丢失的类型转换时发出警告。 25 | 26 | 7. **-Wl,--stack=268435456**:设置生成的可执行文件的堆栈大小为268435456字节(256 MB)。这可以确保你的程序有足够的栈空间来处理递归或大量的局部变量。通常OJ题目所给定的内存限制大小即为栈空间大小,但是前提是OJ的编译命令中有这一条,否则,默认模式下的栈空间大小不超过2MB。 27 | 28 | *** 29 | 30 | ### 白名单相关 31 | 32 | 了解了下原因,发现是电脑杀毒软件的问题(在运行exe前会先扫描一遍),提供两种解决方案: 33 | 34 | 其一是没有安装任何额外的杀毒软件,[参见该链接](https://blog.csdn.net/rb0518/article/details/120553667?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-120553667-blog-104544424.pc_relevant_3mothn_strategy_and_data_recovery&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-120553667-blog-104544424.pc_relevant_3mothn_strategy_and_data_recovery&utm_relevant_index=1);其二是安装了额外的杀毒软件,这里以我装的火绒为例(如图,两步骤完成): 35 | 36 |
37 | 38 |
火绒设置界面
39 |
40 | 41 | 效果对比: 42 | 43 |
44 | 45 |
调整前
46 |
47 | 48 |
49 | 50 |
调整后
51 |
-------------------------------------------------------------------------------- /06 - 个人博客搬运/算法笔记与题单/07 - 树上分治.md: -------------------------------------------------------------------------------- 1 | # 往期浏览 2 | 3 | [第一期 - 博弈论](https://www.cnblogs.com/WIDA/p/16570498.html) 4 | 5 | [第二期 - 前缀和](https://www.cnblogs.com/WIDA/p/15504413.html) 6 | 7 | [第三期 - 二分与三分算法](https://www.cnblogs.com/WIDA/p/17615803.html) 8 | 9 | [第四期 - 莫队算法](https://www.cnblogs.com/WIDA/p/17610403.html) 10 | 11 | [第五期 - 线段树(暂时未公开)]() 12 | 13 | [第六期 - 位运算 (Bitmasks)](https://www.cnblogs.com/WIDA/p/17547678.html) 14 | 15 | [第七期 - 树上分治](https://www.cnblogs.com/WIDA/p/17610362.html) 16 | 17 | [第八期 - Tarjan缩点](https://www.cnblogs.com/WIDA/p/17635152.html) 18 | 19 | [第九期 - 网络流](https://www.cnblogs.com/WIDA/p/17672842.html) 20 | 21 | [第十期 - 字符串哈希](https://www.cnblogs.com/WIDA/p/17766310.html) 22 | 23 | # 讲解 24 | 25 | 点分治:每次选取树的重心进行递归分治,中阶算法,大部分模板不需要理解,但是每一题都需要对维护函数进行修改。复杂度 $\mathcal O(N\log N)$ 。 26 | 27 | # 个人封装 28 | 29 | 由于需要进行一定程度的修改,不符合结构体封装的原则,故没有使用结构体。 30 | 31 | ```c 32 | int root = 0, MaxTree = 1e18; //分别代表重心下标、最大子树大小 33 | vector vis(n + 1), siz(n + 1); 34 | auto get = [&](auto self, int x, int fa, int n) -> void { // 获取树的重心 35 | siz[x] = 1; 36 | int val = 0; 37 | for (auto [y, w] : ver[x]) { 38 | if (y == fa || vis[y]) continue; 39 | self(self, y, x, n); 40 | siz[x] += siz[y]; 41 | val = max(val, siz[y]); 42 | } 43 | val = max(val, n - siz[x]); 44 | if (val < MaxTree) { 45 | MaxTree = val; 46 | root = x; 47 | } 48 | }; 49 | 50 | auto clac = [&](int x) -> void { // 以 x 为新的根,维护询问 51 | 52 | }; 53 | 54 | auto dfz = [&](auto self, int x, int fa) -> void { // 点分治 55 | vis[x] = 1; // 标记已经被更新过的旧重心,确保只对子树分治 56 | clac(x); 57 | for (auto [y, w] : ver[x]) { 58 | if (y == fa || vis[y]) continue; 59 | MaxTree = 1e18; 60 | get(get, y, x, siz[y]); 61 | self(self, root, x); 62 | } 63 | }; 64 | 65 | get(get, 1, 0, n); 66 | dfz(dfz, root, 0); 67 | ``` 68 | 69 | # 题单 70 | 71 | 1. [321C - Ciel the Commander](https://codeforces.com/contest/321/problem/C):思路启蒙题,分治寻找重心; 72 | 2. [P3806 【模板】点分治1](https://www.luogu.com.cn/problem/P3806):洛谷模板题; 73 | 3. [P2634 [国家集训队] 聪聪可可](https://www.luogu.com.cn/problem/P2634):洛谷另一道模板题,与上一题几乎一致; -------------------------------------------------------------------------------- /06 - 个人博客搬运/算法笔记与题单/10 - 字符串哈希.md: -------------------------------------------------------------------------------- 1 | # 往期浏览 2 | 3 | [第一期 - 博弈论](https://www.cnblogs.com/WIDA/p/16570498.html) 4 | 5 | [第二期 - 前缀和](https://www.cnblogs.com/WIDA/p/15504413.html) 6 | 7 | [第三期 - 二分与三分算法](https://www.cnblogs.com/WIDA/p/17615803.html) 8 | 9 | [第四期 - 莫队算法](https://www.cnblogs.com/WIDA/p/17610403.html) 10 | 11 | [第五期 - 线段树(暂时未公开)]() 12 | 13 | [第六期 - 位运算](https://www.cnblogs.com/WIDA/p/17547678.html) 14 | 15 | [第七期 - 树上分治](https://www.cnblogs.com/WIDA/p/17610362.html) 16 | 17 | [第八期 - Tarjan缩点](https://www.cnblogs.com/WIDA/p/17635152.html) 18 | 19 | [第九期 - 网络流](https://www.cnblogs.com/WIDA/p/17672842.html) 20 | 21 | [第十期 - 字符串哈希](https://www.cnblogs.com/WIDA/p/17766310.html) 22 | 23 | # 题单 24 | 25 | ## 1003F($\tt *2200$;字符串-哈希、字符串-KMP、暴力) 26 | 27 | string - hashing List #5 | 比较难受的一题,原先的字符串板子传递变量的时间过慢导致一直超时,但是难度并不是很高。 28 | 29 | 首先观察到单个字符串长度极长但是数量很少,于是想到使用字符串哈希将总长度缩小,属于比较典的思路,使用 `map` 即可轻松做到这一点;随后暴力枚举全部匹配情况后依次统计次数即可,借助 KMP 可以在 $\mathcal O(N^3)$ 完成。注意,如果使用 `find` 函数规避 KMP ,那么理论最坏复杂度会上升到 $\mathcal O(N^4)$,我一开始是由于读错了题,以为至多可替换两段内容,于是没有使用 KMP,导致超时。 30 | 31 | 另外还有一点需要注意的是,上述的复杂度需要简单修改 KMP 函数使之能够对哈希后的数组进行匹配,如果使用 `to_string` 函数对哈希后的数组再处理回字符串后运行 KMP(没错,我一开始真的这样写了),复杂度会上升到最坏 $\mathcal O(N^2 \cdot \sum_{i=1}^{200})$,这样依旧会超时。 32 | 33 | 另外,注意到我们是暴力枚举后进行匹配,未被枚举到的部分其实不需要进行匹配,所以本题有一个修改 KMP 匹配范围进行优化的方法,使得单次只匹配使用到的那部分内容,可以简单学习。根据这一优化方式,抽象了一个“统计原串中某个子串重复出现的次数”的板子。 34 | 35 | ## 7D($\tt *2200$;字符串-哈希) 36 | 37 | string - hashing List #4 | 哈希判回文,较模板。单哈希即可通过,双哈希有超时风险。观察到有较高效的做法 [See](https://codeforces.com/contest/7/submission/226849687) 。 38 | 39 | ## 25E($\tt *2200$;暴力、字符串-哈希、字符串-KMP) 40 | 41 | string - hashing List #3 | 暴力枚举后运行两个字符串算法,较模板。写这题的过程中综合了之前做过的字符串哈希的题目形成了一个较为系统的哈希相关封装。 42 | 43 | ## 1200E($\tt *2000$;字符串、暴力、哈希) 44 | 45 | string - hashing List #2 | 字符串后缀哈希,感觉是很典的题目。 46 | 47 | ## 514C($\tt *2000$;字符串、数据结构-字典树、搜索、暴力、哈希) 48 | 49 | 注意到串仅由三个字母构成。方法一是很显然的,建立字典树后在树上进行搜索匹配串:针对匹配串的每一位,暴力枚举其三种不同构成,最终以 $\mathcal 3\cdot \sum N$ 的复杂度完成搜索。针对该方案有如下优化:$\tt ^1$ 不使用 `auto` 进行搜索,而是直接用单独的函数进行,前者在实现上花费的时间更长一些;数组不留余量,使用 $\tt 0-idx$ 法,这一优化可以提速近乎一倍,推测原因是减少了数组跳转的次数,应当引起注意。 50 | 51 | string - hashing List #1 | 下面介绍字符串方面的方法:字符串哈希。本题使用单哈希需要将模数固定在 $10^{10}$ 量级以上,不然会撞;如果是双哈希则没有特别的限制。属于字符串哈希的模板题,但是需要手写一个 $\mathcal O(1)$ 进行某一位修改的函数(本质上是与方法一暴力修改一样的思路),比较考验对哈希过程原理的理解。 -------------------------------------------------------------------------------- /07 - 论文与资料/【于纪平】C++的pb_ds库在OI中的应用.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hh2048/XCPC/5d09845515bfdb7d2e9e19badd3c5e351f6da2af/07 - 论文与资料/【于纪平】C++的pb_ds库在OI中的应用.pdf -------------------------------------------------------------------------------- /07 - 论文与资料/图的偏心距示意图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hh2048/XCPC/5d09845515bfdb7d2e9e19badd3c5e351f6da2af/07 - 论文与资料/图的偏心距示意图.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | $\textcolor{#70695d}{\LARGE {\pmb {\mathfrak{I\ took\ the\ one\ less\ traveled\ by,}}}}$ 4 | 5 | $\textcolor{#70695d}{\LARGE {\pmb {\mathfrak{and\ that\ has\ made\ all\ the\ difference.}}}}$ 6 | 7 |
8 | 9 | $\mathcal{{\pmb ZJSU\ -AA\ Lab}}$ 10 | 11 | $2020-2024{\rm,\ Created\ \pmb{and}\ maintained\ \pmb{by}\ \mathcal{Wida}.}$ 12 | 13 |
14 | 15 | # 前言 16 | 17 | 这里是 $\mathcal{\pmb{Wida}}$ 的 XCPC 在线模板库。 18 | 19 | 感谢我的两位队友 $\mathcal{\pmb{Hamine}}$ 和 $\pmb{ppafo}$ 在本仓库建立过程中提供的帮助与支持。 20 | 21 | 本文主要分为如下几个大模块: 22 | 23 | - [我个人常用的在线比赛模板](https://github.com/hh2048/XCPC/blob/main/01%20-%20常用在线模板汇总) 24 | - [我个人线下赛会使用的打印稿模板](https://github.com/hh2048/XCPC/blob/main/02%20-%20打印稿模板汇总/README.md) 25 | - [Jiangly 常用的在线比赛模板](https://github.com/hh2048/XCPC/blob/main/03%20-%20jiangly模板收集/README.md) 26 | - [历年XCPC赛事补题链接整理](https://github.com/hh2048/XCPC/blob/main/04%20-%20历年XCPC赛事补题链接整理/README.md) 27 | - [一些个人的博客以及可能有用笔记搬运](https://github.com/hh2048/XCPC/blob/main/05%20-%20个人博客搬运) 28 | 29 | ~~在我退役前会持续维护更新,大约一周一次~~ 已经退役哩,大家有缘再见。 30 | 31 | ## ⭐ Star History 32 | 33 | 34 | Star History Chart 35 | --------------------------------------------------------------------------------