├── LICENSE ├── README.md ├── XCPC算法模板(2024-12-26).pdf └── 算法 ├── 人工智能 ├── Adam算法.cpp ├── Dyna-Q.cpp ├── kmeans聚类.cpp ├── 主成分分析.cpp └── 对抗搜索(Alpha-Beta剪枝).cpp ├── 动态规划 ├── 插头DP.cpp ├── 斯坦纳树(点权).cpp ├── 斯坦纳树(边权).cpp └── 最长上升子序列.cpp ├── 匹配算法 ├── KM算法.cpp ├── 匈牙利算法.cpp └── 带花树算法.cpp ├── 图论 ├── Dijkstra算法.cpp ├── SPFA算法.cpp ├── 一般图最大权匹配.cpp ├── 强连通分量.cpp ├── 拓扑排序.cpp ├── 支配树.cpp ├── 无向图的割顶和桥.cpp ├── 无向图的点双连通分量.cpp ├── 无向图的边双连通分量.cpp ├── 最小树形图.cpp ├── 最小生成树.cpp ├── 有向图的传递闭包.cpp ├── 欧拉回路.cpp └── 确定有限状态自动机最小化.cpp ├── 基础算法 ├── C++17 万能输出.cpp ├── C++20 万能输出.cpp ├── C++光速读入(交互版).cpp ├── C++快速读入.cpp ├── Java快速读入.java ├── RMQ算法.cpp ├── 二分查找.cpp ├── 动态数组.cpp ├── 动态维护连续相同数字区间.cpp ├── 哈希值组合.cpp ├── 哈希表.cpp ├── 基数排序.cpp ├── 带删除的优先队列.cpp ├── 快速离散化.cpp ├── 排列组合枚举.cpp └── 计时器.cpp ├── 字符串算法 ├── AC自动机.cpp ├── KMP算法.cpp ├── LCT维护隐式后缀树.cpp ├── Lyndon分解.cpp ├── Manacher算法.cpp ├── hash.cpp ├── 区间本质不同子串个数.cpp ├── 双端回文自动机.cpp ├── 后缀平衡树.cpp ├── 后缀数组.cpp ├── 后缀树.cpp ├── 后缀自动机.cpp ├── 回文串Border.cpp ├── 回文自动机.cpp ├── 基于后缀自动机构建后缀树.cpp ├── 序列自动机.cpp ├── 扩展KMP.cpp ├── 模糊匹配.cpp └── 非势能分析回文自动机.cpp ├── 数学算法 ├── QR迭代.cpp ├── SG函数.cpp ├── 三分求极值.cpp ├── 二元一次不定方程.cpp ├── 多项式拟合(泰勒展开).py ├── 多项式拟合(辛普森积分).cpp ├── 快速傅里叶变换.cpp ├── 快速幂运算.cpp ├── 欧拉函数.cpp ├── 求解异或方程组.cpp ├── 牛顿迭代求非线性方程组.cpp ├── 矩阵与状态转移.cpp ├── 矩阵求逆.cpp ├── 稀疏矩阵的高斯消元.cpp ├── 线性筛素数.cpp ├── 线性规划.cpp ├── 自适应辛普森积分.cpp ├── 莫比乌斯反演.cpp ├── 行列式计算.cpp ├── 逆元.cpp ├── 递推式求解.cpp ├── 雅可比方法.cpp └── 高斯消元.cpp ├── 数据结构 ├── AVL Tree.cpp ├── Euler-Tour-Tree.cpp ├── KD-Tree.cpp ├── SplayTree.cpp ├── TopTree.cpp ├── Treap.cpp ├── link-cut-tree.cpp ├── link-cut-tree(指针).cpp ├── link-cut-tree(维护子树).cpp ├── link-cut-tree(边权).cpp ├── pb-ds平衡树.cpp ├── rope.cpp ├── zkw线段树(单点加-区间加-单点查询-区间求和).cpp ├── 主席树.cpp ├── 动态主席树.cpp ├── 动态开点线段树.cpp ├── 动态开点线段树(单点加-区间求和-合并).cpp ├── 可修改优先队列.cpp ├── 可持久化Treap.cpp ├── 可持久化并查集.cpp ├── 可持久化数组.cpp ├── 可持久化整数集合.cpp ├── 可持久化线段树.cpp ├── 四分树.cpp ├── 整数集合.cpp ├── 李超线段树.cpp ├── 树状数组.cpp ├── 树链剖分.cpp ├── 树链剖分求LCA和距离.cpp ├── 线段树.cpp └── 线段树(历史最大值).cpp ├── 树算法 ├── O(1)-LCA.cpp ├── 倍增求LCA.cpp ├── 动态点分治.cpp ├── 动态维护树中白色结点的最长距离.cpp ├── 快速求树中与结点x距离不超过k的点权和.cpp ├── 树的欧拉序.cpp ├── 点分治.cpp ├── 虚树.cpp └── 限定距离的子树问题.cpp ├── 网络流 ├── Dinic.cpp ├── HLPP.cpp ├── ISAP.cpp ├── MCMF-dijkstra.cpp └── MCMF-spfa.cpp ├── 莫队算法 ├── 回滚莫队.cpp ├── 带修改莫队.cpp └── 莫队算法.cpp └── 高精度 ├── 分数类.cpp ├── 大整数类.cpp └── 高精度定点数.cpp /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 sunkafei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | 在本科期间,本人获得了XCPC金奖共计8枚,包括EC Final金奖和CCPC Final金奖,区域赛最好成绩为2020CCPC长春站的季军。 3 | 4 | 5 | 目前本人已退役,并将自己的算法模板整理如下,重点是数据结构和字符串,涉及的数论和计算几何较少(队友负责)。 6 | **本算法库持续更新中... 如果你有好的算法模板想加入进来,或者觉得此算法库有bug,欢迎提出pull requests。** 7 | 8 | 此外我还获得过CCSP 金奖,CCCC 一等奖,华为软件精英挑战赛 冠军,华为嵌入式软件大赛算法组 冠军,华为算法大赛高校组 冠军,昇腾AI原生创新算子挑战赛 冠军,ICPC 2023 Online Challenge powered by Huawei 一等奖。 9 | 10 | 11 | 对于想参加这些开放式算法竞赛的同学可以参考一下我的题解和仓库: 12 | - 2025华为软件精英挑战赛 题解 仓库 13 | - 2024华为嵌入式软件大赛算法组 题解 仓库 14 | - 2023华为嵌入式软件大赛算法组 题解&仓库 15 | - 昇腾AI原生创新算子挑战赛S2 题解 仓库 16 | - 昇腾AI原生创新算子挑战赛S1 题解 仓库 17 | - ICPC 2023 Online Challenge powered by Huawei 仓库 18 | 19 | 此外我还会在知乎@孙咖啡上定期更新一些C++性能压榨和C++新特性相关的文章。 20 | 21 | ## 目录 22 | - 图论 23 | - 确定有限状态自动机最小化 24 | - 拓扑排序 25 | - 欧拉回路 26 | - 最小生成树 27 | - Dijkstra算法 28 | - SPFA算法 29 | - 一般图最大权匹配 30 | - 有向图的传递闭包 31 | - 最小树形图 32 | - 支配树 33 | - 强连通分量 34 | - 无向图的割顶和桥 35 | - 无向图的点双连通分量 36 | - 无向图的边双连通分量 37 | - 树算法 38 | - 限定距离的子树问题 39 | - 树的欧拉序 40 | - 倍增求LCA 41 | - 点分治 42 | - 动态点分治 43 | - 快速求树中与结点x距离不超过k的点权和 44 | - 动态维护树中白色结点的最长距离 45 | - 虚树 46 | - O(1)-LCA 47 | - 基础算法 48 | - Java快速读入 49 | - C++快速读入 50 | - C++光速读入(交互版) 51 | - C++17 万能输出 52 | - C++20 万能输出 53 | - RMQ算法 54 | - 哈希表 55 | - 哈希值组合 56 | - 基数排序 57 | - 快速离散化 58 | - 带删除的优先队列 59 | - 排列组合枚举 60 | - 动态维护连续相同数字区间 61 | - 二分查找 62 | - 计时器 63 | - 动态数组 64 | - 匹配算法 65 | - 匈牙利算法 66 | - KM算法 67 | - 带花树算法 68 | - 高精度 69 | - 大整数类 70 | - 分数类 71 | - 高精度定点数 72 | - 动态规划 73 | - 斯坦纳树(点权) 74 | - 斯坦纳树(边权) 75 | - 插头DP 76 | - 最长上升子序列 77 | - 莫队算法 78 | - 莫队算法 79 | - 回滚莫队 80 | - 带修改莫队 81 | - 数据结构 82 | - 可修改优先队列 83 | - AVL Tree 84 | - TopTree 85 | - 可持久化数组 86 | - 四分树 87 | - Treap 88 | - link-cut-tree 89 | - link-cut-tree(指针) 90 | - link-cut-tree(边权) 91 | - link-cut-tree(维护子树) 92 | - 可持久化Treap 93 | - 树链剖分 94 | - 树链剖分求LCA和距离 95 | - 树状数组 96 | - 李超线段树 97 | - 整数集合 98 | - 可持久化整数集合 99 | - 线段树 100 | - 动态开点线段树 101 | - 主席树 102 | - 动态主席树 103 | - 动态开点线段树(单点加-区间求和-合并) 104 | - 可持久化线段树 105 | - 线段树(历史最大值) 106 | - zkw线段树(单点加-区间加-单点查询-区间求和) 107 | - SplayTree 108 | - 可持久化并查集 109 | - KD-Tree 110 | - Euler-Tour-Tree 111 | - rope 112 | - pb-ds平衡树 113 | - 数学算法 114 | - SG函数 115 | - 自适应辛普森积分 116 | - 高斯消元 117 | - 稀疏矩阵的高斯消元 118 | - 求解异或方程组 119 | - 矩阵与状态转移 120 | - 递推式求解 121 | - 快速傅里叶变换 122 | - 快速幂运算 123 | - 莫比乌斯反演 124 | - 逆元 125 | - 欧拉函数 126 | - 线性筛素数 127 | - 三分求极值 128 | - 多项式拟合(辛普森积分) 129 | - 多项式拟合(泰勒展开) 130 | - 雅可比方法 131 | - 矩阵求逆 132 | - 牛顿迭代求非线性方程组 133 | - QR迭代 134 | - 行列式计算 135 | - 二元一次不定方程 136 | - 线性规划 137 | - 网络流 138 | - Dinic 139 | - ISAP 140 | - HLPP 141 | - MCMF-spfa 142 | - MCMF-dijkstra 143 | - 字符串算法 144 | - 后缀树 145 | - 扩展KMP 146 | - AC自动机 147 | - KMP算法 148 | - Manacher算法 149 | - 后缀数组 150 | - 后缀自动机 151 | - 回文自动机 152 | - 回文串Border 153 | - 双端回文自动机 154 | - 非势能分析回文自动机 155 | - 序列自动机 156 | - hash 157 | - LCT维护隐式后缀树 158 | - 区间本质不同子串个数 159 | - Lyndon分解 160 | - 后缀平衡树 161 | - 模糊匹配 162 | - 基于后缀自动机构建后缀树 163 | - 人工智能 164 | - 主成分分析 165 | - Adam算法 166 | - Dyna-Q 167 | - kmeans聚类 168 | - 对抗搜索(Alpha-Beta剪枝) 169 | -------------------------------------------------------------------------------- /XCPC算法模板(2024-12-26).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/XCPC算法模板(2024-12-26).pdf -------------------------------------------------------------------------------- /算法/人工智能/Adam算法.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | const int dim = 2; 10 | using vec = array; 11 | /* 12 | * grad: 计算梯度的函数 13 | * pos: 搜索的起始点 14 | * lr: 学习率(移动的步长) 15 | * decay: 学习率的衰减指数 16 | * exit: 当学习率小于exit时算法结束 17 | * 注意该算法求出的是 函数的极**小**值点 18 | */ 19 | vec Adam(function grad, vec pos, double lr, double decay, double exit, 20 | const double beta1 = 0.9, const double beta2 = 0.9) { 21 | double pw1 = 1, pw2 = 1; 22 | vec m = {}, v = {}; 23 | for (int t = 1; lr >= exit; ++t) { 24 | vec g = grad(pos); 25 | pw1 *= beta1; 26 | pw2 *= beta2; 27 | for (int i = 0; i < dim; ++i) { 28 | m[i] = m[i] * beta1 + g[i] * (1 - beta1); 29 | v[i] = v[i] * beta2 + g[i] * g[i] * (1 - beta2); 30 | double update = m[i] / (1 - pw1); 31 | double factor = lr / (sqrt(v[i] / (1 - pw2)) + 1e-8); 32 | pos[i] -= update * factor; 33 | } 34 | lr *= decay; 35 | } 36 | return pos; 37 | } 38 | 39 | int main() { 40 | auto grad = [](vec A) { 41 | double x = A[0], y = A[1]; 42 | return vec{ 2 * y + -4 / (x * x), 2 * x + -4 / (y * y) }; 43 | }; 44 | auto ans = Adam(grad, vec{ 1, 1 }, 0.01, 0.999, 1e-8); 45 | printf("%.10f %.10f\n", ans[0], ans[1]); //1.2599210498948732 46 | return 0; 47 | } -------------------------------------------------------------------------------- /算法/人工智能/Dyna-Q.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | const int nrow = 10, ncol = 10, max_state = nrow * ncol, max_action = 4; 12 | const int dr[4] = { -1, 1, 0, 0 }; 13 | const int dc[4] = { 0, 0, -1, 1 }; 14 | struct CliffWalkingEnv { 15 | char s[11][11]; 16 | int x, y; 17 | vector> path; 18 | CliffWalkingEnv() { 19 | reset(); 20 | } 21 | int reset() { 22 | x = 0, y = 0; 23 | path.clear(); 24 | path.emplace_back(x, y); 25 | memset(s, 0, sizeof(s)); 26 | for (int i = 0; i < nrow; ++i) { 27 | for (int j = 0; j < ncol; ++j) { 28 | s[i][j] = '.'; 29 | } 30 | } 31 | for (int i = 0; i < 5; ++i) { 32 | for (int j = 1; j < ncol - 1; ++j) { 33 | s[i][j] = '#'; 34 | } 35 | } 36 | return x * ncol + y; 37 | } 38 | void print() { 39 | char t[11][11]; 40 | memcpy(t, s, sizeof(s)); 41 | for (auto [x, y] : path) { 42 | if (t[x][y] >= '1' && t[x][y] < '9') 43 | t[x][y] += 1; 44 | else 45 | t[x][y] = '1'; 46 | } 47 | for (int i = 0; i < nrow; ++i) 48 | printf("%s\n", t[i]); 49 | printf("\n"); 50 | } 51 | tuple step(int number) { 52 | int nx = x + dr[number], ny = y + dc[number]; 53 | if (nx >= 0 && nx < nrow && ny >= 0 && ny < ncol) 54 | x = nx, y = ny; 55 | int reward = -1, done = false; 56 | if (s[x][y] == '#') { 57 | reward = -100; 58 | done = true; 59 | } 60 | if (x == 0 && y == ncol - 1) 61 | done = true; 62 | path.emplace_back(x, y); 63 | return make_tuple(x * ncol + y, reward, done); 64 | } 65 | } env; 66 | 67 | const double alpha = 1e-1; /*学习率*/ 68 | const double gamma = 0.9; /*折扣因子*/ 69 | const double epsilon = 1e-2; /*epsilon-greedy*/ 70 | const int N = 10; //Q-Planning的次数 71 | const int max_episode = 200; //训练多少轮 72 | uniform_real_distribution p; 73 | uniform_int_distribution d; 74 | default_random_engine e; 75 | double Q[max_state][max_action]; 76 | map, int> id; 77 | vector> model; 78 | int epsilon_greedy(int state) { /*基于epsilon-greedy选择动作*/ 79 | if (p(e) < epsilon) 80 | return d(e) % max_action; 81 | return max_element(Q[state], Q[state] + max_action) - Q[state]; 82 | } 83 | void learn(int s0, int a0, int r, int s1) { 84 | auto td_error = r + gamma * *max_element(Q[s1], Q[s1] + max_action) - Q[s0][a0]; 85 | Q[s0][a0] += alpha * td_error; 86 | } 87 | void update(int s0, int a0, int r, int s1) { 88 | learn(s0, a0, r, s1); 89 | pair pr(s0, a0); 90 | if (!id.count(pr)) { 91 | id[pr] = model.size(); 92 | model.emplace_back(s0, a0, r, s1); 93 | } 94 | else { 95 | model[id[pr]] = make_tuple(s0, a0, r, s1); 96 | } 97 | for (int i = 0; i < N; ++i) { 98 | int idx = d(e) % model.size(); 99 | auto [s0, a0, r, s1] = model[idx]; 100 | learn(s0, a0, r, s1); 101 | } 102 | } 103 | void DynaQ() { 104 | for (int i = 0; i < max_episode; ++i) { 105 | int state = env.reset(); 106 | for (;;) { 107 | int action = epsilon_greedy(state); 108 | auto [next_state, reward, done] = env.step(action); 109 | update(state, action, reward, next_state); 110 | state = next_state; 111 | if (done) 112 | break; 113 | } 114 | env.print(); 115 | } 116 | } 117 | int main() { 118 | DynaQ(); 119 | return 0; 120 | } -------------------------------------------------------------------------------- /算法/人工智能/主成分分析.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | const int maxn = 210000; 12 | const int maxdim = 1001; 13 | const double eps = 1e-8; 14 | using matrix = double[maxdim][maxdim]; 15 | using vec = array; 16 | using pair_t = pair; 17 | struct PCA { 18 | matrix A, V; 19 | int column[maxdim], n; 20 | void update(int r, int c, double v) { 21 | A[r][c] = v; 22 | if (column[r] == c || fabs(A[r][c]) > fabs(A[r][column[r]])) { 23 | for (int i = 0; i < n; ++i) if (i != r) 24 | if (fabs(A[r][i]) > fabs(A[r][column[r]])) 25 | column[r] = i; 26 | } 27 | } 28 | void Jacobi() { 29 | for (int i = 0; i < n; ++i) { 30 | for (int j = 0; j < n; ++j) 31 | V[i][j] = 0; 32 | V[i][i] = 1; 33 | column[i] = (i == 0 ? 1 : 0); 34 | } 35 | for (int i = 0; i < n; ++i) 36 | for (int j = 0; j < n; ++j) 37 | if (j != i && fabs(A[i][j]) > fabs(A[i][column[i]])) 38 | column[i] = j; 39 | for (int T = 0; ; ++T) { //迭代次数限制 40 | int x, y; 41 | double val = 0; 42 | for (int i = 0; i < n; ++i) 43 | if (fabs(A[i][column[i]]) > val) 44 | val = fabs(A[i][column[i]]), x = i, y = column[i]; 45 | if (val < eps) //精度限制 46 | break; 47 | double phi = atan2(-2 * A[x][y], A[y][y] - A[x][x]) / 2; 48 | double sinp = sin(phi), cosp = cos(phi); 49 | for (int i = 0; i < n; ++i) if (i != x && i != y) { 50 | double a = A[x][i] * cosp + A[y][i] * sinp; 51 | double b = A[x][i] * -sinp + A[y][i] * cosp; 52 | update(x, i, a); 53 | update(y, i, b); 54 | } 55 | for (int i = 0; i < n; ++i) if (i != x && i != y) { 56 | double a = A[i][x] * cosp + A[i][y] * sinp; 57 | double b = A[i][x] * -sinp + A[i][y] * cosp; 58 | update(i, x, a); 59 | update(i, y, b); 60 | } 61 | for (int i = 0; i < n; ++i) { 62 | double a = V[i][x] * cosp + V[i][y] * sinp; 63 | double b = V[i][x] * -sinp + V[i][y] * cosp; 64 | V[i][x] = a, V[i][y] = b; 65 | } 66 | double a = A[x][x] * cosp * cosp + A[y][y] * sinp * sinp + 2 * A[x][y] * cosp * sinp; 67 | double b = A[x][x] * sinp * sinp + A[y][y] * cosp * cosp - 2 * A[x][y] * cosp * sinp; 68 | double tmp = (A[y][y] - A[x][x]) * sin(2 * phi) / 2 + A[x][y] * cos(2 * phi); 69 | update(x, y, tmp); 70 | update(y, x, tmp); 71 | A[x][x] = a, A[y][y] = b; 72 | } 73 | } 74 | //a 为输入向量组 75 | //n 为向量的维数 76 | //center 指针用来保存输入向量组的中心点 77 | //返回特征值和特征向量的pair,按照特征值从大到小排序 78 | //特征值是各个点在对应特征向量方向的坐标平方和,除以(a.size() - 1)为方差。 79 | auto solve(vector a, int n, vec* center = nullptr) { 80 | this->n = n; 81 | vec s = {}; 82 | for (int i = 0; i < a.size(); ++i) 83 | for (int j = 0; j < n; ++j) 84 | s[j] += a[i][j]; 85 | for (int j = 0; j < n; ++j) 86 | s[j] /= a.size(); 87 | for (int i = 0; i < a.size(); ++i) 88 | for (int j = 0; j < n; ++j) 89 | a[i][j] -= s[j]; 90 | if (center) *center = s; 91 | for (int i = 0; i < n; ++i) { 92 | for (int j = 0; j < n; ++j) { 93 | A[i][j] = 0; 94 | for (int k = 0; k < a.size(); ++k) 95 | A[i][j] += a[k][i] * a[k][j]; 96 | } 97 | } 98 | Jacobi(); 99 | vector result; 100 | for (int i = 0; i < n; ++i) 101 | result.emplace_back(A[i][i], vec()); 102 | for (int i = 0; i < n; ++i) 103 | for (int j = 0; j < n; ++j) 104 | result[i].second[j] = V[j][i]; 105 | sort(result.begin(), result.end(), greater()); 106 | return result; 107 | } 108 | }pca; 109 | int main() { //1329070654.526 110 | freopen("in.txt", "r", stdin); 111 | vector a; 112 | int n, m; 113 | scanf("%d %d", &n, &m); 114 | for (int i = 0; i < n; ++i) { 115 | vec v = {}; 116 | for (int j = 0; j < m; ++j) 117 | scanf("%lf", &v[j]); 118 | a.push_back(v); 119 | } 120 | auto now = clock(); 121 | auto result = pca.solve(a, m); 122 | for (int i = 0; i < m; ++i) 123 | printf("%.3f\n", result[i].first); 124 | printf("time: %f\n", double(clock() - now) / CLOCKS_PER_SEC); 125 | return 0; 126 | } -------------------------------------------------------------------------------- /算法/人工智能/对抗搜索(Alpha-Beta剪枝).cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | struct state { 5 | bool inFinal(); 6 | int score(); 7 | void expand(int, vector&); 8 | }; 9 | int search(state& s, int player, int alpha, int beta) { 10 | if (s.inFinal()) { 11 | return s.score(); 12 | } 13 | vector children; 14 | s.expand(player, children); 15 | for (auto child : children) { 16 | int value = search(child, player ^ 1, alpha, beta); 17 | if (!player) { 18 | alpha = max(alpha, value); 19 | } 20 | else { 21 | beta = min(beta, value); 22 | } 23 | if (beta <= alpha) { 24 | break; 25 | } 26 | } 27 | return !player ? alpha : beta; 28 | } -------------------------------------------------------------------------------- /算法/动态规划/插头DP.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | const int INF = 100000000; 6 | int nrows, ncols; 7 | int G[10][10]; 8 | // 插头编号:0表示无插头,1表示和数字2连通,2表示和数字3连通 9 | struct State { 10 | int up[9]; // up[i](0<=i 0 && left != 0); // 是否必须要有左插头 25 | int must_up = (row > 0 && up[col] != 0); // 是否必须要有上插头 26 | if ((must_left && L != left) || (!must_left && L != 0)) return false; // 左插头不匹配 27 | if ((must_up && U != up[col]) || (!must_up && U != 0)) return false; // 上插头不匹配 28 | if (must_left && must_up && left != up[col]) return false; // 若左插头和上插头都存在,二者必须匹配 29 | // 产生新状态。实际上只有当前列的下插头和left插头有变化 30 | for (int i = 0; i < ncols; i++) T.up[i] = up[i]; 31 | T.up[col] = D; 32 | T.left = R; 33 | return true; 34 | } 35 | }; 36 | int memo[9][9][59049]; // 3^10 37 | // 当前要放置格子(row, col),状态为S。返回最小总长度 38 | int rec(int row, int col, const State& S) { 39 | if (col == ncols) { col = 0; row++; } 40 | if (row == nrows) return 0; 41 | int key = S.encode(); 42 | int& res = memo[row][col][key]; 43 | if (res >= 0) return res; 44 | res = INF; 45 | State T; 46 | if (G[row][col] <= 1) { // 空格(0)或者障碍格(1) 47 | if (S.next(row, col, 0, 0, 0, 0, T)) res = min(res, rec(row, col + 1, T)); // 整个格子里都不连线 48 | if (G[row][col] == 0) // 如果是空格,可以连线。由于线不能分叉,所以这条线一定连接格子的某两个边界(6种情况) 49 | for (int t = 1; t <= 2; t++) { // 枚举线的种类。t=1表示2线,t=2表示3线 50 | if (S.next(row, col, t, t, 0, 0, T)) res = min(res, rec(row, col + 1, T) + 2); // 上<->下 51 | if (S.next(row, col, t, 0, t, 0, T)) res = min(res, rec(row, col + 1, T) + 2); // 上<->左 52 | if (S.next(row, col, t, 0, 0, t, T)) res = min(res, rec(row, col + 1, T) + 2); // 上<->右 53 | if (S.next(row, col, 0, t, t, 0, T)) res = min(res, rec(row, col + 1, T) + 2); // 下<->左 54 | if (S.next(row, col, 0, t, 0, t, T)) res = min(res, rec(row, col + 1, T) + 2); // 下<->右 55 | if (S.next(row, col, 0, 0, t, t, T)) res = min(res, rec(row, col + 1, T) + 2); // 左<->右 56 | } 57 | } 58 | else { 59 | int t = G[row][col] - 1; // 数字为2和3,但插头类型是1和2,所以要减1 60 | // 由于线不能分叉,所以这条线一定连接格子中间的数字和某一个边界(4种情况) 61 | if (S.next(row, col, t, 0, 0, 0, T)) res = min(res, rec(row, col + 1, T) + 1); // 从上边界出来 62 | if (S.next(row, col, 0, t, 0, 0, T)) res = min(res, rec(row, col + 1, T) + 1); // 从下边界出来 63 | if (S.next(row, col, 0, 0, t, 0, T)) res = min(res, rec(row, col + 1, T) + 1); // 从左边界出来 64 | if (S.next(row, col, 0, 0, 0, t, T)) res = min(res, rec(row, col + 1, T) + 1); // 从右边界出来 65 | } 66 | return res; 67 | } 68 | int main() { 69 | while (scanf("%d%d", &nrows, &ncols) == 2 && nrows && ncols) { 70 | for (int i = 0; i < nrows; i++) 71 | for (int j = 0; j < ncols; j++) 72 | scanf("%d", &G[i][j]); 73 | State S; 74 | memset(&S, 0, sizeof(S)); 75 | memset(memo, -1, sizeof(memo)); 76 | int ans = rec(0, 0, S); 77 | if (ans == INF) ans = 0; 78 | printf("%d\n", ans / 2); 79 | } 80 | return 0; 81 | } 82 | /* 83 | void normalize() { 84 | int rep[maxn] = {}, num = 0; 85 | for (int i = 0; i < m; ++i) if (cp[i] > 0) { 86 | if (rep[cp[i]] <= 0) 87 | rep[cp[i]] = ++num; 88 | cp[i] = rep[cp[i]]; 89 | } 90 | if (left > 0) { 91 | if (rep[left] <= 0) 92 | rep[left] = ++num; 93 | left = rep[left]; 94 | } 95 | } 96 | // 把所有编号为b的连通分量改成a 97 | void merge(int a, int b) { 98 | if (a == b) return; 99 | for (int i = 0; i < ncols; i++) 100 | if (comp[i] == b) comp[i] = a; 101 | } 102 | */ -------------------------------------------------------------------------------- /算法/动态规划/斯坦纳树(点权).cpp: -------------------------------------------------------------------------------- 1 | /* 2 | BZOJ2595 3 | 输入一张带有点权的图,求将所有权值为0的结点连接起来所需要的最小费用(最小权值和)。 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | using namespace std; 13 | const int inf = 0x3f3f3f3f; //inf + inf 必须小于 INT_MAX 14 | //如果此处修改inf,那么也应当在init函数中修改dp数组的初始值。 15 | const int maxn = 130; 16 | const int maxedges = 1100; 17 | const int maxstate = 11000; 18 | struct SteinerTree { 19 | int n, k; //n为图中点的个数,k为斯坦纳树的结点数 20 | int dp[maxn][maxstate], st[maxn], val[maxn]; //若点i为点集中的点则st[i]为该点对应的状态,否则为0 21 | int first[maxn], nxt[maxedges * 2], to[maxedges * 2], cur; //图的邻接表 22 | pair pre[maxn][maxstate]; 23 | bool inq[maxn][maxstate], vis[maxn]; 24 | queue Q; 25 | void init(int n, int* w, vector c) { //n为图中的所有结点个数,c为斯坦纳树的结点集合 26 | this->n = n; this->k = c.size(); //w为点权 27 | memset(dp, 0x3f, sizeof(dp)); 28 | memset(pre, 0x3f, sizeof(pre)); 29 | memset(st, 0, sizeof(st)); 30 | for (int i = 0; i < k; ++i) 31 | st[c[i]] = (1 << i); 32 | for (int i = 1; i <= n; ++i) 33 | dp[i][st[i]] = 0; 34 | for (int i = 1; i <= n; ++i) 35 | val[i] = w[i]; 36 | memset(inq, 0, sizeof(inq)); 37 | memset(vis, 0, sizeof(vis)); 38 | while (!Q.empty()) Q.pop(); 39 | memset(first, 0, sizeof(first)); 40 | cur = 0; 41 | } 42 | void add_edge(int u, int v) { 43 | nxt[++cur] = first[u]; 44 | first[u] = cur; 45 | to[cur] = v; 46 | } 47 | void spfa(int s) { //对当前点集状态为s的dp值进行松弛 48 | while (!Q.empty()) { 49 | int u = Q.front(); Q.pop(); 50 | for (int i = first[u]; i; i = nxt[i]) { 51 | int v = to[i]; 52 | if (dp[v][s] > dp[u][s] + val[v]) { 53 | dp[v][s] = dp[u][s] + val[v]; 54 | pre[v][s] = make_pair(u, s); 55 | if (!inq[v][s]) { 56 | Q.push(v); 57 | inq[v][s] = true; 58 | } 59 | } 60 | } 61 | inq[u][s] = false; 62 | } 63 | } 64 | void solve() { //斯坦纳树的权值和为min{dp[i][(1 << k) - 1]}, 1 <= i <= n 65 | for (int j = 1; j < (1 << k); ++j) { 66 | for (int i = 1; i <= n; ++i) { 67 | for (int sub = (j - 1) & j; sub; sub = (sub - 1) & j) { 68 | int x = sub, y = j - sub; 69 | int t = dp[i][x] + dp[i][y] - val[i]; 70 | if (dp[i][j] > t) { 71 | dp[i][j] = t; 72 | pre[i][j] = make_pair(i, sub); 73 | } 74 | } 75 | if (dp[i][j] < inf) { 76 | Q.push(i); 77 | inq[i][j] = true; 78 | } 79 | } 80 | spfa(j); 81 | } 82 | } 83 | void dfs(int i, int state) { 84 | if (i == inf || pre[i][state].second == 0) 85 | return; 86 | vis[i] = 1; //vis[i]表示结点i存在于斯坦纳树中 87 | pair pr = pre[i][state]; 88 | dfs(pr.first, pr.second); 89 | if (pr.first == i) 90 | dfs(i, state - pr.second); 91 | } 92 | 93 | }tree; 94 | int A[maxn][maxn], weight[maxn], dr[] = { -1, 1, 0, 0 }, dc[] = { 0, 0, -1, 1 }; 95 | int main() { 96 | #define node(i, j) ((i) * m + (j) + 1) 97 | //freopen("in.txt", "r", stdin); 98 | int n, m, nd = -1; 99 | scanf("%d %d", &n, &m); 100 | vector c; 101 | for (int i = 0; i < n; ++i) { 102 | for (int j = 0; j < m; ++j) { 103 | scanf("%d", &A[i][j]); 104 | weight[node(i, j)] = A[i][j]; 105 | if (A[i][j] == 0) { 106 | c.push_back(node(i, j)); 107 | if (nd == -1) nd = node(i, j); 108 | } 109 | } 110 | } 111 | tree.init(n * m, weight, c); 112 | for (int i = 0; i < n; ++i) { 113 | for (int j = 0; j < m; ++j) { 114 | for (int k = 0; k < 4; ++k) { 115 | int x = i + dr[k], y = j + dc[k]; 116 | if (x < 0 || y < 0 || x >= n || y >= m) continue; 117 | tree.add_edge(node(i, j), node(x, y)); 118 | } 119 | } 120 | } 121 | tree.solve(); 122 | printf("%d\n", tree.dp[nd][(1 << c.size()) - 1]); 123 | tree.dfs(nd, (1 << c.size()) - 1); 124 | for (int i = 0; i < n; i++) { 125 | for (int j = 0; j < m; j++) { 126 | if (!A[i][j]) printf("x"); 127 | else if (tree.vis[node(i, j)]) printf("o"); 128 | else printf("_"); 129 | if (j == m - 1) printf("\n"); 130 | } 131 | } 132 | return 0; 133 | } -------------------------------------------------------------------------------- /算法/动态规划/斯坦纳树(边权).cpp: -------------------------------------------------------------------------------- 1 | /* 2 | HDU4085 3 | 给定n个点,前k个点属于集合A,后k个点属于集合B。点之间通过m条带权边相连。 4 | 要求选出权值和最小的边集使得A集合中的每个点都可以与B中的某个点进行配对(若a能走到b则可以考虑是使其配对)。 5 | 解决方案:将集合A与集合B中的点作为关键点求出斯坦纳树,因为最后的结果可以是一个森林,所以我们在进行一次DP。 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | using namespace std; 15 | const int inf = 0x3f3f3f3f; //inf + inf 必须小于 INT_MAX 16 | //如果此处修改inf,那么也应当在init函数中修改dp数组的初始值。 17 | const int maxn = 110; 18 | const int maxedges = 1100; 19 | const int maxstate = 11000; 20 | struct SteinerTree{ 21 | int n, k; //n为图中点的个数,k为斯坦纳树的结点数 22 | int dp[maxn][maxstate], st[maxn]; //若点i为点集中的点则st[i]为该点对应的状态,否则为0 23 | int first[maxn], nxt[maxedges * 2], to[maxedges * 2], weight[maxedges * 2], cur; //图的邻接表 24 | bool inq[maxn][maxstate]; 25 | queue Q; 26 | void init(int n, vector c) { //n为图中的所有结点个数,c为斯坦纳树的结点集合 27 | this->n = n; this->k = c.size(); 28 | memset(dp, 0x3f, sizeof(dp)); 29 | memset(st, 0, sizeof(st)); 30 | for(int i = 0; i < k; ++i) 31 | st[c[i]] = (1 << i); 32 | for(int i = 1; i <= n; ++i) 33 | dp[i][st[i]] = 0; 34 | memset(inq, 0, sizeof(inq)); 35 | while(!Q.empty()) Q.pop(); 36 | memset(first, 0, sizeof(first)); 37 | cur = 0; 38 | } 39 | void add_edge(int u, int v, int w) { 40 | nxt[++cur] = first[u]; 41 | first[u] = cur; 42 | to[cur] = v; 43 | weight[cur] = w; 44 | } 45 | void spfa(int s) { //对当前点集状态为s的dp值进行松弛 46 | while (!Q.empty()) { 47 | int u = Q.front(); Q.pop(); 48 | inq[u][s] = false; 49 | for (int i = first[u]; i; i = nxt[i]) { 50 | int v = to[i], w = weight[i]; 51 | int state = st[v] | s; 52 | if (dp[v][state] > dp[u][s] + w) { 53 | dp[v][state] = dp[u][s] + w; 54 | if (state != s || inq[v][s]) 55 | continue; 56 | Q.push(v); 57 | inq[v][s] = true; 58 | } 59 | } 60 | } 61 | } 62 | void solve() { //斯坦纳树的权值和为min{dp[i][(1 << k) - 1]}, 1 <= i <= n 63 | for (int j = 1; j < (1 << k); ++j) { 64 | for (int i = 1; i <= n; ++i) { 65 | if (st[i] && (st[i] & j) == 0) 66 | continue; 67 | for (int sub = (j - 1) & j; sub; sub = (sub - 1) & j) { 68 | int x = st[i] | sub, y = st[i] | (j - sub); 69 | dp[i][j] = min(dp[i][j], dp[i][x] + dp[i][y]); 70 | } 71 | if (dp[i][j] != inf) { 72 | Q.push(i); 73 | inq[i][j] = true; 74 | } 75 | } 76 | spfa(j); 77 | } 78 | /* 79 | int ans = inf; 80 | for (int i = 1; i <= n; ++i) 81 | ans = min(ans, tree.dp[i][(1 << k) - 1]); 82 | */ 83 | } 84 | }tree; 85 | 86 | int n, m, k, d[maxstate]; 87 | bool check(int s) { //当且仅当状态s中A集合与B集合结点个数相等时,才是合法状态 88 | int res = 0; 89 | for (int i = 0; s; i++, s >>= 1) 90 | res += (s & 1) * (i < k ? 1 : -1); 91 | return (res == 0 ? true : false); 92 | } 93 | int main() { 94 | //freopen("in.txt", "r", stdin); 95 | int T; 96 | scanf("%d", &T); 97 | while (T--) { 98 | scanf("%d %d %d", &n, &m, &k); 99 | vector c; 100 | for (int i = 1; i <= k; ++i) 101 | c.push_back(i); 102 | for (int i = 1; i <= k; ++i) 103 | c.push_back(n - k + i); 104 | tree.init(n, c); 105 | for (int i = 0; i < m; ++i) { 106 | int u, v, w; 107 | scanf("%d %d %d", &u, &v, &w); 108 | tree.add_edge(u, v, w); 109 | tree.add_edge(v, u, w); 110 | } 111 | tree.solve(); 112 | const int mask = (1 << (2 * k)) - 1; 113 | for (int s = 0; s <= mask; s++) { 114 | d[s] = inf; 115 | for (int i = 1; i <= n; i++) 116 | d[s] = min(d[s], tree.dp[i][s]); //初始值为状态s中的点连成一棵树的情况 117 | } 118 | for (int s = 1; s <= mask; s++) if (check(s)) 119 | for (int p = (s - 1) & s; p; p = (p - 1) & s) if (check(p)) 120 | d[s] = min(d[s], d[p] + d[s - p]); //考虑将树分解为森林 121 | if (d[mask] >= inf) 122 | puts("No solution"); 123 | else 124 | printf("%d\n", d[mask]); 125 | } 126 | return 0; 127 | } -------------------------------------------------------------------------------- /算法/动态规划/最长上升子序列.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | const int maxn = 110000; 8 | const int inf = 1 << 30; 9 | //d[i]表示以下标i结尾的最长上升子序列长度 10 | int a[maxn], g[maxn], d[maxn], n; 11 | int main() { 12 | scanf("%d", &n); 13 | for (int i = 1; i <= n; ++i) 14 | scanf("%d", &a[i]); 15 | for (int i = 1; i <= n; ++i) 16 | g[i] = inf; 17 | for (int i = 1; i <= n; ++i) { 18 | int k = lower_bound(g + 1, g + n + 1, a[i]) - g; 19 | g[k] = a[i]; 20 | d[i] = k; 21 | } 22 | int ans = *max_element(d + 1, d + n + 1); 23 | printf("%d\n", ans); 24 | return 0; 25 | } -------------------------------------------------------------------------------- /算法/匹配算法/KM算法.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | const long long inf = 1LL << 60; 9 | const int maxn = 505; 10 | //若要求不完美匹配,需要把所有的-inf替换成0。 11 | struct KM { 12 | int n, py[maxn], vy[maxn], pre[maxn]; 13 | long long G[maxn][maxn], slk[maxn], kx[maxn], ky[maxn]; 14 | void init(int n) { //左右两侧各有n个结点,结点编号从1开始 15 | this->n = n; 16 | for (int i = 1; i <= n; ++i) 17 | for (int j = 1; j <= n; ++j) 18 | G[i][j] = -inf; 19 | for (int i = 1; i <= n; ++i) 20 | ky[i] = py[i] = 0; 21 | } 22 | void add_edge(int x, int y, long long w) { 23 | G[y][x] = max(G[y][x], w); 24 | } 25 | long long solve() { 26 | int k, p; 27 | for (int i = 1; i <= n; ++i) 28 | kx[i] = *max_element(G[i] + 1, G[i] + n + 1); 29 | for (int i = 1; i <= n; i++) { 30 | for (int j = 0; j <= n; ++j) 31 | vy[j] = pre[j] = 0, slk[j] = inf; 32 | for (py[k = 0] = i; py[k]; k = p) { 33 | long long d = inf; 34 | int x = py[k]; 35 | vy[k] = 1; 36 | for (int j = 1; j <= n; j++) if (!vy[j]) { 37 | long long t = kx[x] + ky[j] - G[x][j]; 38 | if (t < slk[j]) 39 | slk[j] = t, pre[j] = k; 40 | if (slk[j] < d) 41 | d = slk[j], p = j; 42 | } 43 | for (int j = 0; j <= n; j++) { 44 | if (vy[j]) 45 | kx[py[j]] -= d, ky[j] += d; 46 | else 47 | slk[j] -= d; 48 | } 49 | } 50 | for (; k; k = pre[k]) 51 | py[k] = py[pre[k]]; 52 | } 53 | long long ans = 0; 54 | for (int i = 1; i <= n; i++) if (G[py[i]][i] > -inf) 55 | ans += kx[i] + ky[i]; 56 | return ans; 57 | } 58 | vector result() { //返回左侧每个结点对应的右侧匹配点,没有则为0 59 | vector res(1);//调用该函数之前,应当先调用solve函数 60 | for (int i = 1; i <= n; ++i) 61 | res.push_back(G[py[i]][i] > -inf ? py[i] : 0); 62 | return res; 63 | } 64 | }km; 65 | struct KM_old { 66 | static const int inf = 1 << 30; 67 | int n; 68 | vector G[maxn]; 69 | int W[maxn][maxn]; 70 | int lx[maxn], ly[maxn]; 71 | int left[maxn]; 72 | bool S[maxn], T[maxn]; 73 | void init(int n) { 74 | this->n = n; 75 | for (int i = 0; i < n; ++i) 76 | G[i].clear(); 77 | memset(W, 0, sizeof(W)); 78 | } 79 | void add_edge(int u, int v, int w) { 80 | G[u].push_back(v); 81 | W[u][v] = w; 82 | } 83 | bool match(int u) { 84 | S[u] = true; 85 | for (unsigned i = 0; i < G[u].size(); ++i) { 86 | int v = G[u][i]; 87 | if (lx[u] + ly[v] == W[u][v] && !T[v]) { 88 | T[v] = true; 89 | if (left[v] == -1 || match(left[v])) { 90 | left[v] = u; 91 | return true; 92 | } 93 | } 94 | } 95 | return false; 96 | } 97 | void update() { 98 | int a = inf; 99 | for (int u = 0; u < n; ++u) if (S[u]) { 100 | for (unsigned i = 0; i < G[u].size(); ++i) { 101 | int v = G[u][i]; 102 | if (!T[v]) a = min(a, lx[u] + ly[v] - W[u][v]); 103 | } 104 | } 105 | for (int i = 0; i < n; ++i) { 106 | if (S[i]) lx[i] -= a; 107 | if (T[i]) ly[i] += a; 108 | } 109 | } 110 | int solve() { 111 | for (int i = 0; i < n; ++i) { 112 | lx[i] = *max_element(W[i], W[i] + n); 113 | left[i] = -1; 114 | ly[i] = 0; 115 | } 116 | for (int u = 0; u < n; ++u) { 117 | for (;;) { 118 | for (int i = 0; i < n; ++i) S[i] = T[i] = false; 119 | if (match(u)) break; else update(); 120 | } 121 | } 122 | int ans = 0; 123 | for (int i = 0; i < n; ++i) if (left[i] != -1) 124 | ans += W[left[i]][i]; 125 | return ans; 126 | } 127 | }; 128 | int main() { 129 | int n, m; 130 | scanf("%d %d", &n, &m); 131 | km.init(n); 132 | for (int i = 1; i <= m; ++i) { 133 | int x, y, w; 134 | scanf("%d %d %d", &x, &y, &w); 135 | km.add_edge(y, x, w); 136 | } 137 | printf("%lld\n", km.solve()); 138 | auto res = km.result(); 139 | for (int i = 1; i <= n; ++i) 140 | printf("%d ", res[i]); 141 | printf("\n"); 142 | return 0; 143 | } -------------------------------------------------------------------------------- /算法/匹配算法/匈牙利算法.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/匹配算法/匈牙利算法.cpp -------------------------------------------------------------------------------- /算法/匹配算法/带花树算法.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | const int maxn = 1005; 10 | struct Blossom { 11 | int mate[maxn], nxt[maxn], pa[maxn], st[maxn], vis[maxn], t, n; 12 | int ban[maxn]; 13 | vector G[maxn]; 14 | queue Q; 15 | void init(int n) { //编号从0开始 16 | this->n = n; 17 | this->t = 0; 18 | memset(mate, -1, sizeof(mate)); 19 | memset(ban, 0, sizeof(ban)); 20 | for (int i = 0; i < n; ++i) 21 | G[i].clear(); 22 | } 23 | inline void add_edge(int x, int y) { //添加双向边(x, y) 24 | G[x].push_back(y); 25 | G[y].push_back(x); 26 | } 27 | inline int find(int x) { 28 | return pa[x] == x ? x : pa[x] = find(pa[x]); 29 | } 30 | inline void merge(int a, int b) { 31 | pa[find(a)] = find(b); 32 | } 33 | int lca(int x, int y) { 34 | for (t++;;swap(x, y)) if (~x) { 35 | if (vis[x = find(x)] == t) 36 | return x; 37 | vis[x] = t; 38 | x = ~mate[x] ? nxt[mate[x]] : -1; 39 | } 40 | } 41 | void group(int a, int p) { 42 | for (int b, c; a != p; merge(a, b), merge(b, c), a = c) { 43 | b = mate[a], c = nxt[b]; 44 | if (find(c) != p) 45 | nxt[c] = b; 46 | if (st[b] == 2) 47 | st[b] = 1, Q.push(b); 48 | if (st[c] == 2) 49 | st[c] = 1, Q.push(c); 50 | } 51 | } 52 | void augment(int s) { 53 | for (int i = 0; i < n; ++i) 54 | nxt[i] = vis[i] = -1, pa[i] = i, st[i] = 0; 55 | Q = queue(); 56 | Q.push(s); 57 | st[s] = 1; 58 | while (mate[s] == -1 && !Q.empty()) { 59 | int x = Q.front();Q.pop(); 60 | for (auto y : G[x]) { 61 | if (!ban[y] && y != mate[x] && find(x) != find(y) && st[y] != 2) { 62 | if (st[y] == 1) { 63 | int p = lca(x, y); 64 | if (find(x) != p) 65 | nxt[x] = y; 66 | if (find(y) != p) 67 | nxt[y] = x; 68 | group(x, p); 69 | group(y, p); 70 | } 71 | else if (mate[y] == -1) { 72 | nxt[y] = x; 73 | for (int j = y, k, i; ~j; j = i) 74 | k = nxt[j], i = mate[k], mate[j] = k, mate[k] = j; 75 | break; 76 | } 77 | else 78 | nxt[y] = x, Q.push(mate[y]), st[mate[y]] = 1, st[y] = 2; 79 | } 80 | } 81 | } 82 | } 83 | void solve() { //求最大匹配 84 | for (int i = 0; i < n; ++i) if (mate[i] == -1) //从所有的点开始增广 85 | augment(i); 86 | int ans = 0; 87 | for (int i = 0; i < n; ++i) if (mate[i] != -1) 88 | ans++; 89 | printf("%d\n", ans / 2); //匹配数 90 | for (int i = 0; i < n; ++i) //打印每个点的匹配点,-1表示未匹配 91 | printf("%d ", mate[i]); 92 | printf("\n"); 93 | } 94 | vector unnecessary() { //求出所有的非必要匹配点(存在一个最大匹配不包含这个点) 95 | for (int i = 0; i < n; ++i) if (mate[i] == -1)//先求出最大匹配 96 | augment(i); 97 | vector ret; 98 | for (int x = 0; x < n; ++x) { 99 | if (mate[x] == -1) 100 | ret.push_back(x); 101 | else { 102 | int y = mate[x]; 103 | mate[y] = -1; 104 | mate[x] = -1; 105 | ban[x] = 1; 106 | augment(y); 107 | ban[x] = 0; 108 | if (mate[y] != -1) 109 | ret.push_back(x); 110 | else 111 | augment(y); 112 | } 113 | } 114 | return ret; 115 | } 116 | }mp; 117 | int main() { 118 | int n, m; 119 | scanf("%d %d", &n, &m); 120 | mp.init(n); 121 | for (int i = 0; i < m; ++i) { 122 | int x, y; 123 | scanf("%d %d", &x, &y); --x; --y; 124 | mp.add_edge(x, y); 125 | } 126 | mp.solve(); 127 | return 0; 128 | } -------------------------------------------------------------------------------- /算法/图论/Dijkstra算法.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | using namespace std; 10 | int n, m, s; //点数、边数、起点 11 | namespace simple_dijkstra { // O(n^2) 12 | const int maxn = 1001; 13 | int vis[maxn], d[maxn], w[maxn][maxn]; 14 | void dijkstra() { 15 | memset(vis, 0, sizeof(vis)); 16 | memset(d, 0x3f, sizeof(d)); 17 | d[s] = 0; 18 | for (int i = 1; i <= n; ++i) { 19 | int x = -1; 20 | for (int y = 1; y <= n; ++y) if (!vis[y]) 21 | if (x == -1 || d[y] < d[x]) 22 | x = y; 23 | vis[x] = true; 24 | for (int y = 1; y <= n; ++y) 25 | d[y] = min(d[y], d[x] + w[x][y]); 26 | } 27 | } 28 | void solve() { 29 | scanf("%d %d %d", &n, &m, &s); 30 | memset(w, 0x3f, sizeof(w)); 31 | for (int i = 0; i < m; ++i) { 32 | int x, y, z; 33 | scanf("%d %d %d", &x, &y, &z); 34 | w[x][y] = min(w[x][y], z); 35 | } 36 | dijkstra(); 37 | for (int i = 1; i <= n; ++i) 38 | printf("%d ", d[i] == 0x3f3f3f3f ? INT_MAX : d[i]); 39 | printf("\n"); 40 | } 41 | } 42 | namespace fast_dijkstra { 43 | const int maxn = 210000; 44 | vector> G[maxn]; 45 | int d[maxn]; 46 | void dijkstra() { 47 | using node = pair; 48 | priority_queue, greater> Q; 49 | memset(d, 0x3f, sizeof(d)); 50 | d[s] = 0; 51 | Q.emplace(0, s); 52 | while (!Q.empty()) { 53 | auto [dist, x] = Q.top(); Q.pop(); 54 | if (dist != d[x]) 55 | continue; 56 | for (auto [y, w] : G[x]) { 57 | if (d[y] > d[x] + w) { 58 | d[y] = d[x] + w; 59 | Q.emplace(d[y], y); 60 | //p[y] = x; 61 | } 62 | } 63 | } 64 | } 65 | void solve() { 66 | scanf("%d %d %d", &n, &m, &s); 67 | for (int i = 0; i < m; ++i) { 68 | int x, y, z; 69 | scanf("%d %d %d", &x, &y, &z); 70 | G[x].emplace_back(y, z); 71 | } 72 | dijkstra(); 73 | for (int i = 1; i <= n; ++i) 74 | printf("%d ", d[i] == 0x3f3f3f3f ? INT_MAX : d[i]); 75 | printf("\n"); 76 | } 77 | } 78 | int main() { 79 | //freopen("in.txt", "r", stdin); 80 | fast_dijkstra::solve(); 81 | return 0; 82 | } -------------------------------------------------------------------------------- /算法/图论/SPFA算法.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | using namespace std; 10 | int n, m, s; //点数、边数、起点 11 | namespace BellmanFord { // O(nm) 12 | const int maxn = 1001; 13 | const int maxedges = 110000; 14 | int d[maxn], x[maxedges], y[maxedges], w[maxedges]; 15 | void BellmanFord() { 16 | memset(d, 0x3f, sizeof(d)); 17 | d[s] = 0; 18 | for (int k = 1; k < n; ++k) 19 | for (int i = 1; i <= m; ++i) 20 | d[y[i]] = min(d[y[i]], d[x[i]] + w[i]); 21 | } 22 | void solve() { 23 | scanf("%d %d %d", &n, &m, &s); 24 | for (int i = 1; i <= m; ++i) 25 | scanf("%d %d %d", &x[i], &y[i], &w[i]); 26 | BellmanFord(); 27 | for (int i = 1; i <= n; ++i) 28 | printf("%d ", d[i] == 0x3f3f3f3f ? INT_MAX : d[i]); 29 | printf("\n"); 30 | } 31 | } 32 | namespace SPFA { 33 | const int maxn = 210000; 34 | int inq[maxn], cnt[maxn], d[maxn]; 35 | vector> G[maxn]; 36 | bool spfa() { 37 | queue Q; 38 | memset(inq, 0, sizeof(inq)); 39 | memset(cnt, 0, sizeof(cnt)); 40 | memset(d, 0x3f, sizeof(d)); 41 | d[s] = 0; 42 | inq[s] = true; 43 | Q.push(s); 44 | while (!Q.empty()) { 45 | int x = Q.front(); Q.pop(); 46 | inq[x] = false; 47 | for (auto [y, w] : G[x]) { 48 | if (d[y] > d[x] + w) { 49 | d[y] = d[x] + w; 50 | //p[y] = x; 51 | if (!inq[y]) { 52 | Q.push(y); 53 | inq[y] = true; 54 | if (++cnt[y] > n) 55 | return false; 56 | } 57 | } 58 | } 59 | } 60 | return true; 61 | } 62 | void solve() { 63 | scanf("%d %d %d", &n, &m, &s); 64 | for (int i = 1; i <= m; ++i) { 65 | int x, y, w; 66 | scanf("%d %d %d", &x, &y, &w); 67 | G[x].emplace_back(y, w); 68 | } 69 | spfa(); 70 | for (int i = 1; i <= n; ++i) 71 | printf("%d ", d[i] == 0x3f3f3f3f ? INT_MAX : d[i]); 72 | printf("\n"); 73 | } 74 | } 75 | int main() { 76 | //freopen("in.txt", "r", stdin); 77 | SPFA::solve(); 78 | return 0; 79 | } -------------------------------------------------------------------------------- /算法/图论/强连通分量.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/图论/强连通分量.cpp -------------------------------------------------------------------------------- /算法/图论/拓扑排序.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | const int maxn = 110000; 10 | int n, m, degree[maxn]; 11 | vector G[maxn]; 12 | vector toposort1() { 13 | queue Q; 14 | for (int i = 1; i <= n; ++i) 15 | degree[i] = 0; 16 | for (int x = 1; x <= n; ++x) 17 | for (auto y : G[x]) 18 | degree[y]++; 19 | for (int i = 1; i <= n; ++i) if (degree[i] == 0) 20 | Q.push(i); 21 | vector res; 22 | while (!Q.empty()) { 23 | int x = Q.front(); Q.pop(); 24 | res.push_back(x); 25 | for (auto y : G[x]) { 26 | degree[y]--; 27 | if (degree[y] == 0) 28 | Q.push(y); 29 | } 30 | } 31 | return res; 32 | } 33 | vector result; 34 | int vis[maxn]; 35 | void dfs(int x) { 36 | vis[x] = true; 37 | for (auto y : G[x]) if (!vis[y]) 38 | dfs(y); 39 | result.push_back(x); 40 | } 41 | vector toposort2() { //必须保证是有向无环图 42 | memset(vis, 0, sizeof(vis)); 43 | for (int i = 1; i <= n; ++i) if (!vis[i]) 44 | dfs(i); 45 | reverse(result.begin(), result.end()); 46 | return result; 47 | } 48 | int main() { //uva 10305 49 | //freopen("in.txt", "r", stdin); 50 | while (scanf("%d %d", &n, &m) == 2) { 51 | if (n == 0 && m == 0) 52 | break; 53 | for (int i = 1; i <= n; ++i) 54 | G[i].clear(); 55 | for (int i = 0; i < m; ++i) { 56 | int x, y; 57 | scanf("%d %d", &x, &y); 58 | G[x].push_back(y); 59 | } 60 | auto ans = toposort2(); 61 | printf("%d", ans[0]); 62 | for (int i = 1; i < n; ++i) 63 | printf(" %d", ans[i]); 64 | printf("\n"); 65 | } 66 | return 0; 67 | } -------------------------------------------------------------------------------- /算法/图论/支配树.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 在调用dominator_tree::add_edge之前要先调用init函数进行初始化,init的参数n表示支配树中有n的点,编号为1-n。 3 | idom[x]表示点x的最近支配点, idom[root] == 0。 4 | head保存为原图的链表。 5 | back保存为反图的链表。 6 | dfn[x]表示结点x的DFS序。 7 | id[i]表示DFS序为i的结点编号。 8 | tarjan(s)可以求出以s为起点的支配树。 9 | 性质:dfn[x] > dfn[idom[x]],即结点x的DFS序一定大于它的支配点的DFS序 10 | 因此可以按照DFS序从小到大枚举点x,此时它在支配树上的父结点已经计算完毕。 11 | for (int i = 1; i <= n; ++i) { 12 | int x = id[i]; 13 | ans[x] = F(ans[idom[x]], x); 14 | } 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | using namespace std; 22 | const int maxn = 210000; 23 | const int maxedges = 310000; 24 | struct dominator_tree { 25 | int n, cnt, tot, head[maxn], fa[maxn], p[maxn], top[maxn], back[maxn], val[maxn], dfn[maxn], id[maxn], semi[maxn], idom[maxn]; 26 | struct edge { 27 | int to, next; 28 | }e[maxedges * 3]; //min: maxedges * 2 + maxn 29 | inline bool cmp(int x, int y) { 30 | return dfn[semi[x]] > dfn[semi[y]]; 31 | } 32 | inline void ins(int* first, int from, int to) { 33 | e[++cnt].to = to; 34 | e[cnt].next = first[from]; 35 | first[from] = cnt; 36 | } 37 | void add_edge(int x, int y) { 38 | ins(head, x, y); 39 | ins(back, y, x); 40 | } 41 | void dfs(int x) { 42 | id[dfn[x] = ++tot] = x; 43 | for (int i = head[x]; i; i = e[i].next) 44 | if (!dfn[e[i].to]) 45 | fa[e[i].to] = x, dfs(e[i].to); 46 | } 47 | int find(int x) { 48 | if (p[x] == x) return x; 49 | int y = find(p[x]); 50 | if (cmp(val[x], val[p[x]])) 51 | val[x] = val[p[x]]; 52 | return p[x] = y; 53 | } 54 | void tarjan(int s) { 55 | dfs(s); 56 | for (int i = tot; i > 1; --i) { 57 | int u = id[i]; 58 | for (int j = back[u]; j; j = e[j].next) { 59 | if (dfn[e[j].to]) { 60 | find(e[j].to); 61 | if (cmp(u, val[e[j].to])) 62 | semi[u] = semi[val[e[j].to]]; 63 | } 64 | } 65 | ins(top, semi[u], u); 66 | p[u] = fa[u]; 67 | u = fa[u]; 68 | for (int j = top[u]; j; j = e[j].next) { 69 | find(e[j].to); 70 | if (semi[val[e[j].to]] == semi[u]) 71 | idom[e[j].to] = u; 72 | else 73 | idom[e[j].to] = val[e[j].to]; 74 | } 75 | top[u] = 0; 76 | } 77 | for (int i = 2; i <= tot; ++i) { 78 | int x = id[i]; 79 | if (idom[x] != semi[x]) 80 | idom[x] = idom[idom[x]]; 81 | } 82 | } 83 | void init(int n) { 84 | this->n = n; 85 | cnt = tot = 0; 86 | memset(head, 0, sizeof(int) * (n + 2)); 87 | memset(back, 0, sizeof(int) * (n + 2)); 88 | memset(top, 0, sizeof(int) * (n + 2)); 89 | memset(dfn, 0, sizeof(int) * (n + 2)); 90 | memset(idom, 0, sizeof(int) * (n + 2)); 91 | for (int i = 1; i <= n; ++i) 92 | val[i] = semi[i] = p[i] = i; 93 | } 94 | }tree; 95 | long long ans[maxn]; 96 | int main() { 97 | //freopen("in.txt", "r", stdin); 98 | int n, m; 99 | scanf("%d %d", &n, &m); 100 | tree.init(n); 101 | for (int i = 1; i <= m; ++i) { 102 | int u, v; 103 | scanf("%d %d", &u, &v); 104 | tree.add_edge(u, v); 105 | } 106 | tree.tarjan(1); 107 | for (int i = 1; i <= n; ++i) 108 | ans[i] = 1; 109 | for (int i = n; i >= 1; --i) { 110 | int x = tree.id[i]; 111 | ans[tree.idom[x]] += ans[x]; 112 | } 113 | for (int i = 1; i <= n; ++i) 114 | printf("%d ", ans[i]); 115 | printf("\n"); 116 | return 0; 117 | } -------------------------------------------------------------------------------- /算法/图论/无向图的割顶和桥.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/图论/无向图的割顶和桥.cpp -------------------------------------------------------------------------------- /算法/图论/无向图的点双连通分量.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/图论/无向图的点双连通分量.cpp -------------------------------------------------------------------------------- /算法/图论/无向图的边双连通分量.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/图论/无向图的边双连通分量.cpp -------------------------------------------------------------------------------- /算法/图论/最小树形图.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/图论/最小树形图.cpp -------------------------------------------------------------------------------- /算法/图论/最小生成树.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | const int maxn = 210000; 8 | int n, m, from[maxn], to[maxn], weight[maxn]; 9 | int p[maxn], r[maxn]; 10 | int find(int x) { 11 | return p[x] == x ? x : p[x] = find(p[x]); 12 | } 13 | int kruskal() { 14 | for (int i = 1; i <= n; ++i) 15 | p[i] = i; 16 | for (int i = 1; i <= m; ++i) 17 | r[i] = i; 18 | sort(r + 1, r + m + 1, [](int a, int b) { 19 | return weight[a] < weight[b]; 20 | }); 21 | int ans = 0, edges = 0; 22 | for (int i = 1; i <= m; ++i) { 23 | int e = r[i]; 24 | int x = find(from[e]), y = find(to[e]); 25 | if (x != y) { 26 | p[x] = y; 27 | ans += weight[e]; 28 | edges += 1; 29 | } 30 | } 31 | if (edges != n - 1) 32 | return -1; //无解 33 | else 34 | return ans; 35 | } 36 | int main() { //洛谷 P3366 37 | //freopen("in.txt", "r", stdin); 38 | scanf("%d %d", &n, &m); 39 | for (int i = 1; i <= m; ++i) 40 | scanf("%d %d %d", &from[i], &to[i], &weight[i]); 41 | int ans = kruskal(); 42 | if (ans == -1) 43 | puts("orz"); 44 | else 45 | printf("%d\n", ans); 46 | return 0; 47 | } -------------------------------------------------------------------------------- /算法/图论/有向图的传递闭包.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | namespace Closure { //求有向图的传递闭包 10 | const int n = 4000; //对于n = 4000,可以在大约1s的时间内求解 11 | bitset A[n]; 12 | void solve(int M[][n]) { //M是一个01矩阵,M[i][j]为1当且仅当有一条从i到j的有向边 13 | for (int i = 0; i < n; ++i) 14 | for (int j = 0; j < n; ++j) 15 | A[i][j] = M[i][j]; 16 | for (int k = 0; k < n; ++k) 17 | for (int i = 0; i < n; ++i) 18 | if (A[i][k]) 19 | A[i] |= A[k]; 20 | for (int i = 0; i < n; ++i) 21 | for (int j = 0; j < n; ++j) 22 | M[i][j] = A[i][j]; //将结果复制回输入矩阵中 23 | } 24 | } 25 | const int maxn = 4000; 26 | int A[maxn][maxn], B[maxn][maxn]; 27 | int main() { 28 | for (int i = 0; i < maxn; ++i) 29 | for (int j = 0; j < maxn; ++j) 30 | A[i][j] = B[i][j] = rand() % 2; 31 | clock_t start = clock(); 32 | for (int k = 0; k < maxn; ++k) 33 | for (int i = 0; i < maxn; ++i) 34 | for (int j = 0; j < maxn; ++j) 35 | B[i][j] = (B[i][j] || (B[i][k] && B[k][j])); 36 | clock_t end = clock(); 37 | printf("%.5f\n", (double)(end - start) / CLOCKS_PER_SEC); 38 | start = clock(); 39 | Closure::solve(A); 40 | end = clock(); 41 | printf("%.5f\n", (double)(end - start) / CLOCKS_PER_SEC); 42 | for (int i = 0; i < maxn; ++i) 43 | for (int j = 0; j < maxn; ++j) 44 | if (A[i][j] != B[i][j]) 45 | abort(); 46 | return 0; 47 | } -------------------------------------------------------------------------------- /算法/图论/欧拉回路.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 1. 无向图有欧拉路径的充要条件是:图联通且至多有两个奇点。 3 | 若不存在奇点,则有欧拉回路。 4 | 2. 有向图有欧拉路径的充要条件是:至多有两个点的入度不等于出度, 5 | 且必须是一个点的出度比入度大1(起点),一个点的入度比出度大1(终点), 6 | 且忽略边的方向后图必须连通。 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | using namespace std; 17 | const int maxn = 110000; 18 | int t, n, m; 19 | namespace direct_graph { 20 | //若有多组数据应将G和result清空,调用完euler之后G中的边会被删除 21 | vector G[maxn]; 22 | vector> result; 23 | void dfs(int x) { 24 | while (G[x].size()) { 25 | auto y = G[x].back(); 26 | G[x].pop_back(); 27 | dfs(y); 28 | result.emplace_back(x, y); 29 | } 30 | } 31 | bool euler(int s) { 32 | dfs(s); 33 | reverse(result.begin(), result.end()); 34 | if (result.size() != m || (result.size() && result.back().second != s)) 35 | return false; //若只要求有欧拉道路则:if (result.size() != m) return false; 36 | for (int i = 1; i < result.size(); ++i) if (result[i - 1].second != result[i].first) 37 | return false; 38 | return true; 39 | } 40 | void solve() { 41 | multimap, int> id; 42 | for (int i = 1; i <= m; ++i) { 43 | int x, y; 44 | scanf("%d %d", &x, &y); 45 | G[x].push_back(y); 46 | id.emplace(make_pair(x, y), i); 47 | } 48 | int s = 1; while (G[s].empty() && s <= n) s++; 49 | if (!euler(s)) { 50 | puts("NO"); 51 | return; 52 | } 53 | else { 54 | puts("YES"); 55 | for (auto pr : result) { 56 | int x = pr.first, y = pr.second; 57 | auto iter = id.find({ x, y }); 58 | if (iter == id.end()) { 59 | iter = id.find({ y, x }); 60 | printf("%d ", -iter->second); 61 | } 62 | else 63 | printf("%d ", iter->second); 64 | id.erase(iter); 65 | } 66 | } 67 | } 68 | } 69 | namespace undirect_graph { 70 | //若有多组数据应将G和result清空,调用完euler之后G中的边会被删除 71 | multiset G[maxn]; 72 | vector> result; 73 | void dfs(int x) { 74 | while (G[x].size()) { 75 | auto iter = G[x].begin(); 76 | auto y = *iter; 77 | G[x].erase(iter); 78 | G[y].erase(G[y].find(x)); 79 | dfs(y); 80 | result.emplace_back(x, y); 81 | } 82 | } 83 | bool euler(int s) { 84 | dfs(s); 85 | reverse(result.begin(), result.end()); 86 | if (result.size() != m || (result.size() && result.back().second != s)) 87 | return false; //若只要求有欧拉道路则:if (result.size() != m) return false; 88 | for (int i = 1; i < result.size(); ++i) if (result[i - 1].second != result[i].first) 89 | return false; 90 | return true; 91 | } 92 | void solve() { 93 | multimap, int> id; 94 | for (int i = 1; i <= m; ++i) { 95 | int x, y; 96 | scanf("%d %d", &x, &y); 97 | G[x].insert(y); 98 | G[y].insert(x); 99 | id.emplace(make_pair(x, y), i); 100 | } 101 | int s = 1; while (G[s].empty() && s <= n) s++; 102 | if (!euler(s)) { 103 | puts("NO"); 104 | return; 105 | } 106 | else { 107 | puts("YES"); 108 | for (auto pr : result) { 109 | int x = pr.first, y = pr.second; 110 | auto iter = id.find({ x, y }); 111 | if (iter == id.end()) { 112 | iter = id.find({ y, x }); 113 | printf("%d ", -iter->second); 114 | } 115 | else 116 | printf("%d ", iter->second); 117 | id.erase(iter); 118 | } 119 | } 120 | } 121 | } 122 | int main() { //uoj 117 123 | //freopen("in.txt", "r", stdin); 124 | scanf("%d %d %d", &t, &n, &m); 125 | if (t == 2) { //有向图 126 | direct_graph::solve(); 127 | } 128 | else { 129 | undirect_graph::solve(); 130 | } 131 | return 0; 132 | } -------------------------------------------------------------------------------- /算法/图论/确定有限状态自动机最小化.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | const int maxn = 110000, sigma_size = 20; 12 | struct Automaton { 13 | int n, m, cur, G[maxn][sigma_size]; 14 | int inq[maxn], cls[maxn]; 15 | vector from[maxn][sigma_size]; 16 | unordered_set equiv[maxn]; //equiv[i]表示编号为i的等价类的集合 17 | void init(int n, int m, vector final) { //状态数、输入字符集大小、每个结点是否为终态 18 | //如果要多次使用该算法,需要在此处先将状态清空。 19 | this->n = n; 20 | this->m = m; 21 | this->cur = 2; 22 | for (int i = 0; i < n; ++i) { 23 | int st = final[i] ^ 1; 24 | equiv[st].insert(i); 25 | cls[i] = st; 26 | } 27 | } 28 | void add_edge(int x, int c, int y) { //添加一条从x指向y的边,字符为c 29 | G[x][c] = y; 30 | } 31 | int minimize() { //调用该方法之前应该先将不可达状态从自动机中删除 32 | for (int i = 0; i < n; ++i) 33 | for (int c = 0; c < m; ++c) 34 | from[G[i][c]][c].push_back(i); 35 | queue Q; 36 | Q.push(0); inq[0] = true; 37 | while (!Q.empty()) { 38 | int x = Q.front(); Q.pop(); //x是一个等价类 39 | inq[x] = false; 40 | for (int c = 0; c < m; ++c) { 41 | unordered_map> par; //class -> set of states 42 | for (auto i : equiv[x]) for (auto u : from[i][c]) 43 | //if (cls[u] != x) 44 | par[cls[u]].push_back(u); 45 | for (auto &[id, member] : par) if (member.size() != equiv[id].size()) { 46 | int now = cur++; 47 | for (auto y : member) { 48 | equiv[id].erase(y); 49 | equiv[now].insert(y); 50 | cls[y] = now; 51 | } 52 | if (inq[id] || equiv[now].size() < equiv[id].size()) 53 | Q.push(now), inq[now] = true; 54 | else 55 | Q.push(id), inq[id] = true; 56 | } 57 | } 58 | } 59 | return cur; //返回合并之后的总状态数 60 | } 61 | } am; 62 | int main() { 63 | int b, m; 64 | scanf("%d %d", &b, &m); 65 | vector final(m, 0); final[0] = 1; 66 | am.init(m, b, move(final)); 67 | for (int i = 0; i < m; ++i) 68 | for (int j = 0; j < b; ++j) 69 | am.add_edge(i, j, (i * b + j) % m); 70 | int sz = am.minimize(); 71 | printf("%d 0\n", sz); 72 | printf("G"); 73 | for (int i = 1; i < sz; ++i) 74 | printf(" B"); 75 | printf("\n"); 76 | for (int i = 0; i < sz; ++i) { 77 | for (int j = 0; j < b; ++j) { 78 | int first = *am.equiv[i].begin(); 79 | printf("%d ", am.cls[(first * b + j) % m]); 80 | } 81 | printf("\n"); 82 | } 83 | return 0; 84 | } -------------------------------------------------------------------------------- /算法/基础算法/C++17 万能输出.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | template void print_element(const T& param); 9 | template void print_element(const std::pair& param); 10 | template void print_element(const std::tuple& param); 11 | template void print_element(const T(¶m)[SIZE]); 12 | template void print_element(const std::vector& param); 13 | template void print_element(const std::set& param); 14 | template void print_element(const std::map& param); 15 | template void print_element(const std::unordered_set& param); 16 | template void print_element(const std::unordered_map& param); 17 | template void print_element(const T& param) { 18 | std::cout << param; 19 | } 20 | template void print_element(const std::pair& param) { 21 | std::cout << '('; 22 | print_element(param.first); 23 | print_element(param.second); 24 | std::cout << ')'; 25 | } 26 | template void print_element(const std::tuple& param) { 27 | std::cout << '('; 28 | std::apply([](auto&&... args) { 29 | (..., (print_element(args), std::cout << ' ')); 30 | }, param); 31 | std::cout << "\b)"; 32 | } 33 | template void print_element(const T(¶m)[SIZE]) { 34 | std::cout << '['; 35 | for (int i = 0; i < SIZE; ++i) { 36 | if (i) { 37 | std::cout << ' '; 38 | } 39 | print_element(param[i]); 40 | } 41 | std::cout << ']'; 42 | } 43 | template void print_element(const std::vector& param) { 44 | std::cout << '['; 45 | for (int i = 0; i < param.size(); ++i) { 46 | if (i) { 47 | std::cout << ' '; 48 | } 49 | print_element(param[i]); 50 | } 51 | std::cout << ']'; 52 | } 53 | template void print_element(const std::set& param) { 54 | std::cout << '{'; 55 | for (auto iter = param.begin(); iter != param.end(); ++iter) { 56 | if (iter != param.begin()) { 57 | std::cout << ' '; 58 | } 59 | print_element(*iter); 60 | } 61 | std::cout << '}'; 62 | } 63 | template void print_element(const std::map& param) { 64 | std::cout << '{'; 65 | for (auto iter = param.begin(); iter != param.end(); ++iter) { 66 | if (iter != param.begin()) { 67 | std::cout << ' '; 68 | } 69 | print_element(*iter); 70 | } 71 | std::cout << '}'; 72 | } 73 | template void print_element(const std::unordered_set& param) { 74 | std::cout << '{'; 75 | for (auto iter = param.begin(); iter != param.end(); ++iter) { 76 | if (iter != param.begin()) { 77 | std::cout << ' '; 78 | } 79 | print_element(*iter); 80 | } 81 | std::cout << '}'; 82 | } 83 | template void print_element(const std::unordered_map& param) { 84 | std::cout << '{'; 85 | for (auto iter = param.begin(); iter != param.end(); ++iter) { 86 | if (iter != param.begin()) { 87 | std::cout << ' '; 88 | } 89 | print_element(*iter); 90 | } 91 | std::cout << '}'; 92 | } 93 | template void print(const T&... params) { 94 | (..., (print_element(params), std::cout << " ")); 95 | std::cout << std::endl; 96 | } 97 | int main() { 98 | auto PR = std::make_tuple(123, "aa", std::vector(3, 0.5)); 99 | std::unordered_set S{ 6, 7, 8 }; 100 | std::vector> V(2, S); 101 | print(PR, V); 102 | return 0; 103 | } -------------------------------------------------------------------------------- /算法/基础算法/C++光速读入(交互版).cpp: -------------------------------------------------------------------------------- 1 | // 每次读取/输出一行,适用于处理交互题。 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | namespace io { 9 | constexpr int MAXBUFFER = 1024 * 1024 * 8; 10 | char ibuffer[MAXBUFFER], *iptr, obuffer[MAXBUFFER], *optr; 11 | inline void start_reading() { // 开始读取新的一行 12 | fgets(ibuffer, sizeof(ibuffer), stdin); 13 | iptr = ibuffer; 14 | } 15 | inline void start_writing() { //开始输出新的一行 16 | optr = obuffer; 17 | } 18 | inline int read_int() { //读入有符号整数 19 | char* nxt; 20 | int ret = strtol(iptr, &nxt, 10); 21 | iptr = nxt; 22 | return ret; 23 | } 24 | inline double read_double() noexcept { // 读入浮点数 25 | char* nxt; 26 | double ret = strtod(iptr, &nxt); 27 | iptr = nxt; 28 | return ret; 29 | } 30 | inline void write_int(int val) { //输出有符号整数,输出完一行后需要调用flush。 31 | char tmp[32], *now = tmp + 20; 32 | int length = 1; 33 | if (val < 0) { 34 | *optr++ = '-'; 35 | val *= -1; 36 | } 37 | *now = ' '; 38 | do { 39 | *--now = '0' + val % 10; 40 | val /= 10; 41 | length += 1; 42 | } while (val > 0); 43 | memcpy(optr, now, length); 44 | optr += length; 45 | } 46 | inline void flush() { 47 | if (optr != obuffer) { 48 | optr[-1] = '\n'; 49 | } 50 | fwrite(obuffer, 1, optr - obuffer, stdout); 51 | fflush(stdout); 52 | } 53 | } 54 | int main() { 55 | io::start_reading(); 56 | double val = io::read_double(); 57 | printf("%.6f\n", val); 58 | return 0; 59 | } -------------------------------------------------------------------------------- /算法/基础算法/C++快速读入.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | inline int read_positive() { //读取一个正整数 4 | int x = 0; 5 | char c = getchar(); 6 | while (c < '0' || c > '9') 7 | c = getchar(); 8 | while (c >= '0' && c <= '9') { 9 | x = x * 10 + c - '0'; 10 | c = getchar(); 11 | } 12 | return x; 13 | } 14 | inline int read_negative() { //可以读取负数 15 | int x = 0, y = 1; 16 | char c = getchar(); 17 | while (c < '0' || c > '9') { 18 | if (c == '-') 19 | y = -1; 20 | c = getchar(); 21 | } 22 | while (c >= '0' && c <= '9') { 23 | x = x * 10 + c - '0'; 24 | c = getchar(); 25 | } 26 | return x * y; 27 | } 28 | int main() { 29 | 30 | } -------------------------------------------------------------------------------- /算法/基础算法/Java快速读入.java: -------------------------------------------------------------------------------- 1 | import java.io.*; 2 | class InputReader { 3 | public BufferedReader reader; 4 | public StringTokenizer tokenizer; 5 | public InputReader(InputStream stream) { 6 | reader = new BufferedReader(new InputStreamReader(stream), 32768); 7 | tokenizer = null; 8 | } 9 | public String next() { 10 | while (tokenizer == null || !tokenizer.hasMoreTokens()) { 11 | try { 12 | tokenizer = new StringTokenizer(reader.readLine()); 13 | } catch (IOException e) { 14 | throw new RuntimeException(e); 15 | } 16 | } 17 | return tokenizer.nextToken(); 18 | } 19 | public int nextInt() { 20 | return Integer.parseInt(next()); 21 | } 22 | } -------------------------------------------------------------------------------- /算法/基础算法/RMQ算法.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | const int maxn = 110000, maxlog = 20; 9 | int d[maxn][maxlog], lg[maxn], A[maxn]; 10 | void preprocess(int n) { //下标范围[1, n],调用之前应当完成数组A的赋值 11 | lg[1] = 0; 12 | for (int i = 2; i <= n; ++i) 13 | lg[i] = lg[i >> 1] + 1; 14 | for (int i = 1; i <= n; ++i) 15 | d[i][0] = A[i]; 16 | for (int j = 1; j <= lg[n]; ++j) 17 | for (int i = 1; i + (1 << j) - 1 <= n; ++i) 18 | d[i][j] = max(d[i][j - 1], d[i + (1 << (j - 1))][j - 1]); 19 | } 20 | inline int rmq(int L, int R) { 21 | int k = lg[R - L + 1]; 22 | return max(d[L][k], d[R - (1 << k) + 1][k]); 23 | } 24 | int main() { 25 | return 0; 26 | } -------------------------------------------------------------------------------- /算法/基础算法/二分查找.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/基础算法/二分查找.cpp -------------------------------------------------------------------------------- /算法/基础算法/动态数组.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | template 5 | class darray : public std::ranges::view_interface> { 6 | private: 7 | T data[maxsize]; 8 | int sz; 9 | public: 10 | darray() : sz(0) {} 11 | darray(const darray& rhs) noexcept : sz(rhs.sz) { 12 | copy(rhs.begin(), rhs.begin() + sz, data); 13 | } 14 | darray& operator= (const darray& rhs) noexcept { 15 | sz = rhs.sz; 16 | copy(rhs.begin(), rhs.begin() + sz, data); 17 | return *this; 18 | } 19 | void push_back(T val) noexcept { 20 | assert(sz < maxsize); 21 | data[sz++] = std::move(val); 22 | } 23 | void expand() noexcept { 24 | assert(sz < maxsize); 25 | sz += 1; 26 | } 27 | void erase(T val) noexcept { 28 | for (int i = 0; i < sz; ++i) { 29 | if (data[i] == val) { 30 | data[i] = std::move(data[sz - 1]); 31 | sz -= 1; 32 | return; 33 | } 34 | } 35 | } 36 | void replace(T old_val, T new_val) noexcept { 37 | for (int i = 0; i < sz; ++i) { 38 | if (data[i] == old_val) { 39 | data[i] = new_val; 40 | return; 41 | } 42 | } 43 | } 44 | void set(T val) noexcept { 45 | data[0] = val; 46 | sz = 1; 47 | } 48 | bool contains(T val) const noexcept { 49 | for (int i = 0; i < sz; ++i) { 50 | if (data[i] == val) { 51 | return true; 52 | } 53 | } 54 | return false; 55 | } 56 | void pop_back() { 57 | assert(sz > 0); 58 | sz -= 1; 59 | } 60 | void clear() noexcept { 61 | sz = 0; 62 | } 63 | auto* begin() noexcept { 64 | return data; 65 | } 66 | auto* end() noexcept { 67 | return data + sz; 68 | } 69 | const auto* begin() const noexcept { 70 | return data; 71 | } 72 | const auto* end() const noexcept { 73 | return data + sz; 74 | } 75 | }; 76 | int main() { 77 | darray arr; 78 | for (int i = 0; i < 50; ++i) { 79 | arr.push_back(i); 80 | } 81 | for (int i = 1; i < 50; ++i) { 82 | arr[i] += arr[i - 1]; 83 | } 84 | std::printf("%d %d %d\n", (int)arr.size(), arr.front(), arr[30]); 85 | return 0; 86 | } -------------------------------------------------------------------------------- /算法/基础算法/动态维护连续相同数字区间.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 动态维护由相同数字构成的区间,A是原始数组。将A中相同数字只保留最左侧,其余设为-1得到val数组。 3 | 该算法动态维护val数组,时间复杂度O(1)。 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | const int maxn = 210000; 12 | int A[maxn], val[maxn], n; 13 | void build() { 14 | 15 | } 16 | inline void add(int index) { //将A[index]添加到val[index]中构成一个新的区间 17 | val[index] = A[index]; 18 | //此处添加代码维护数据结构 19 | } 20 | inline void del(int index) { //删除val[index],也就是删除下标为index的区间 21 | val[index] = -1; 22 | //此处添加代码维护数据结构 23 | } 24 | void init() { //调用init函数初始化之前需要读入n和A 25 | A[0] = A[n + 1] = -1; 26 | val[1] = A[1]; 27 | for (int i = 2; i <= n; ++i) 28 | val[i] = (A[i] == A[i - 1] ? -1 : A[i]); 29 | build(); //用val数组构建数据结构 30 | } 31 | void change(int index, int v) { //将A中下标为index的位置的元素修改为v,函数会维护A和val数组 32 | if (A[index] == v) 33 | return; 34 | if (A[index] == A[index + 1]) 35 | add(index + 1); 36 | if (val[index] != -1) 37 | del(index); 38 | if (val[index + 1] == v) 39 | del(index + 1); 40 | A[index] = v; 41 | if (A[index - 1] != v) 42 | add(index); 43 | } 44 | int main() { 45 | freopen("in.txt", "r", stdin); 46 | n = 5; 47 | for (int i = 1; i <= n; ++i) 48 | A[i] = 1; 49 | init(); 50 | for (int i = 1; i <= n; ++i) printf("%d ", val[i]); printf("\n"); 51 | change(3, 2); for (int i = 1; i <= n; ++i) printf("%d ", val[i]); printf("\n"); 52 | change(4, 2); for (int i = 1; i <= n; ++i) printf("%d ", val[i]); printf("\n"); 53 | change(2, 2); for (int i = 1; i <= n; ++i) printf("%d ", val[i]); printf("\n"); 54 | change(2, 1); for (int i = 1; i <= n; ++i) printf("%d ", val[i]); printf("\n"); 55 | change(1, 2); for (int i = 1; i <= n; ++i) printf("%d ", val[i]); printf("\n"); 56 | change(2, 2); for (int i = 1; i <= n; ++i) printf("%d ", val[i]); printf("\n"); 57 | change(3, 1); for (int i = 1; i <= n; ++i) printf("%d ", val[i]); printf("\n"); 58 | change(3, 2); for (int i = 1; i <= n; ++i) printf("%d ", val[i]); printf("\n"); 59 | return 0; 60 | } -------------------------------------------------------------------------------- /算法/基础算法/哈希值组合.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | template inline size_t hash_combine(const F& first, const T&... params) { 5 | if constexpr (sizeof...(params) == 0) { 6 | return std::hash()(first); 7 | } 8 | else { 9 | size_t seed = hash_combine(params...); 10 | return std::hash()(first) + 0x9e3779b9 + (seed << 6) + (seed >> 2); 11 | } 12 | } 13 | template 14 | struct std::hash> { 15 | inline size_t operator() (const std::pair& pr) const { 16 | return hash_combine(pr.first, pr.second); 17 | } 18 | }; 19 | template 20 | struct std::hash> { 21 | inline size_t operator() (const std::tuple& tp) const { 22 | return apply(hash_combine, tp); // 需要C++17 23 | } 24 | }; 25 | int main() { 26 | using namespace std::literals; 27 | std::unordered_set> S; 28 | S.insert({ 1, 2, "asd"s }); 29 | S.insert({ 4, 5, "zxc"s }); 30 | for (auto [a, b, c] : S) { 31 | std::cout << a << " " << b << " " << c << std::endl; 32 | } 33 | // 1 2 asd 34 | // 4 5 zxc 35 | return 0; 36 | } -------------------------------------------------------------------------------- /算法/基础算法/哈希表.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | using namespace std; 13 | //key_t应当为整数类型,且实际值必须非负 14 | template struct hash_table { 15 | static const int maxn = 1000010; 16 | static const int table_size = 11110007; 17 | int first[table_size], nxt[maxn], sz; //init: memset(first, 0, sizeof(first)), sz = 0 18 | key_t id[maxn]; 19 | type data[maxn]; 20 | type& operator[] (key_t key) { 21 | const int h = key % table_size; 22 | for (int i = first[h]; i; i = nxt[i]) 23 | if (id[i] == key) 24 | return data[i]; 25 | int pos = ++sz; 26 | nxt[pos] = first[h]; 27 | first[h] = pos; 28 | id[pos] = key; 29 | return data[pos] = type(); 30 | } 31 | bool count(key_t key) { 32 | for (int i = first[key % table_size]; i; i = nxt[i]) 33 | if (id[i] == key) 34 | return true; 35 | return false; 36 | } 37 | type get(key_t key) { //如果key对应的值不存在,则返回type()。 38 | for (int i = first[key % table_size]; i; i = nxt[i]) 39 | if (id[i] == key) 40 | return data[i]; 41 | return type(); 42 | } 43 | }; 44 | 45 | unordered_map A; 46 | hash_table B; 47 | const int maxn = 1000000; 48 | int main() { 49 | default_random_engine e; 50 | uniform_int_distribution d(0, LLONG_MAX); 51 | for (int i = 0; i < maxn; ++i) { 52 | long long key = d(e); 53 | long long value = d(e); 54 | A[key] = value; 55 | B[key] = value; 56 | } 57 | for (int i = 0; i < maxn * 10; ++i) { 58 | long long key = d(e); 59 | if (A.count(key) != B.count(key)) 60 | abort(); 61 | if (A.count(key)) { 62 | if (A[key] != B.get(key)) 63 | abort(); 64 | } 65 | } 66 | return 0; 67 | } -------------------------------------------------------------------------------- /算法/基础算法/基数排序.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | using namespace std; 10 | const int maxn = 1100000; //maxn应当大于data数组的大小 11 | namespace RadixSort_Int { 12 | const int base = (1 << 17) - 1; 13 | int c[base + 10], tmp[maxn]; 14 | void sort(int* data, int n) { //只能用于对非负数离散化,若要处理负数, 15 | for (int i = 0; i < 32; i += 16) { //可以将所有的值都加上一个特别大的数使其变为非负数然后再离散化 16 | memset(c, 0, sizeof(c)); 17 | for (int j = 0; j < n; ++j) 18 | c[(data[j] >> i) & base]++; 19 | for (int j = 1; j <= base; ++j) 20 | c[j] += c[j - 1]; 21 | for (int j = n - 1; j >= 0; --j) 22 | tmp[--c[(data[j] >> i) & base]] = data[j]; 23 | for (int j = 0; j < n; ++j) 24 | data[j] = tmp[j]; 25 | } 26 | } 27 | } 28 | namespace RadixSort_LLong { 29 | const int base = (1 << 17) - 1; 30 | int c[base + 10]; 31 | long long tmp[maxn]; 32 | void sort(long long* data, int n) { //只能用于对非负数离散化,若要处理负数, 33 | for (int i = 0; i < 64; i += 16) { //可以将所有的值都加上一个特别大的数使其变为非负数然后再离散化 34 | memset(c, 0, sizeof(c)); 35 | for (int j = 0; j < n; ++j) 36 | c[(data[j] >> i) & base]++; 37 | for (int j = 1; j <= base; ++j) 38 | c[j] += c[j - 1]; 39 | for (int j = n - 1; j >= 0; --j) 40 | tmp[--c[(data[j] >> i) & base]] = data[j]; 41 | for (int j = 0; j < n; ++j) 42 | data[j] = tmp[j]; 43 | } 44 | } 45 | } 46 | int A[maxn], B[maxn]; 47 | int main() { 48 | int n = 1000000; 49 | default_random_engine e; 50 | uniform_int_distribution d(0, INT_MAX); 51 | for (int i = 0; i < n; ++i) 52 | A[i] = B[i] = d(e); 53 | printf("start....\n"); 54 | auto start = clock(); 55 | sort(A, A + n); 56 | printf("std::sort: %f\n", static_cast(clock() - start) / CLOCKS_PER_SEC); 57 | start = clock(); 58 | RadixSort_Int::sort(B, n); 59 | printf("std::sort: %f\n", static_cast(clock() - start) / CLOCKS_PER_SEC); 60 | for (int i = 0; i < n; ++i) 61 | if (A[i] != B[i]) 62 | abort(); 63 | return 0; 64 | } -------------------------------------------------------------------------------- /算法/基础算法/带删除的优先队列.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | template class heap { 10 | private: 11 | priority_queue Q, R; 12 | public: 13 | void maintain() { 14 | while (!R.empty() && Q.top() == R.top()) 15 | Q.pop(), R.pop(); 16 | } 17 | void push(const T& val) { 18 | Q.push(val); 19 | } 20 | void erase(const T& val) { //只能删除还在优先队列中的值,不能删除不存在的值。 21 | R.push(val); 22 | maintain(); 23 | } 24 | void pop() { 25 | Q.pop(); 26 | maintain(); 27 | } 28 | T top() { 29 | return Q.top(); 30 | } 31 | T top2() { //返回第二大的值,调用之前必须保证有size() >= 2。 32 | auto val = Q.top(); pop(); 33 | auto sec = Q.top(); push(val); 34 | return sec; 35 | } 36 | auto size() const { 37 | return Q.size() - R.size(); 38 | } 39 | bool empty() const { 40 | return size() == 0; 41 | } 42 | template void emplace(F&&... args) { 43 | Q.emplace(std::forward(args)...); 44 | } 45 | }; 46 | int main() { 47 | 48 | return 0; 49 | } -------------------------------------------------------------------------------- /算法/基础算法/快速离散化.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | using namespace std; 10 | const int maxn = 11000000; //maxn应当大于data数组的大小 11 | namespace Discretization_Int { 12 | const int base = (1 << 17) - 1; 13 | int c[base + 10]; 14 | pair data[maxn], tmp[maxn]; 15 | void discretization(int* input, int n) { //只能用于对非负数离散化,若要处理负数, 16 | for (int i = 0; i < n; ++i) //可以将所有的值都加上一个特别大的数使其变为非负数然后再离散化 17 | data[i] = make_pair(input[i], i); 18 | for (int i = 0; i < 32; i += 16) { 19 | memset(c, 0, sizeof(c)); 20 | for (int j = 0; j < n; ++j) 21 | c[(data[j].first >> i) & base]++; 22 | for (int j = 1; j <= base; ++j) 23 | c[j] += c[j - 1]; 24 | for (int j = n - 1; j >= 0; --j) 25 | tmp[--c[(data[j].first >> i) & base]] = data[j]; 26 | for (int j = 0; j < n; ++j) 27 | data[j] = tmp[j]; 28 | } 29 | for (int i = 0, j = -1; i < n; ++i) { 30 | if (i == 0 || data[i].first != data[i - 1].first) 31 | ++j; 32 | input[data[i].second] = j; 33 | } 34 | } 35 | } 36 | namespace Discretization_LLong { 37 | const int base = (1 << 17) - 1; 38 | int c[base + 10]; 39 | pair data[maxn], tmp[maxn]; 40 | void discretization(long long* input, int n) { //只能用于对非负数离散化,若要处理负数, 41 | for (int i = 0; i < n; ++i) //可以将所有的值都加上一个特别大的数使其变为非负数然后再离散化 42 | data[i] = make_pair(input[i], i); 43 | for (int i = 0; i < 64; i += 16) { 44 | memset(c, 0, sizeof(c)); 45 | for (int j = 0; j < n; ++j) 46 | c[(data[j].first >> i) & base]++; 47 | for (int j = 1; j <= base; ++j) 48 | c[j] += c[j - 1]; 49 | for (int j = n - 1; j >= 0; --j) 50 | tmp[--c[(data[j].first >> i) & base]] = data[j]; 51 | for (int j = 0; j < n; ++j) 52 | data[j] = tmp[j]; 53 | } 54 | for (int i = 0, j = -1; i < n; ++i) { 55 | if (i == 0 || data[i].first != data[i - 1].first) 56 | ++j; 57 | input[data[i].second] = j; 58 | } 59 | } 60 | } 61 | long long A[maxn], B[maxn], tmp[maxn]; 62 | int main() { 63 | int n = 10000000; 64 | default_random_engine e; 65 | uniform_int_distribution d(0, LLONG_MAX); 66 | e.seed(time(0)); 67 | for (int i = 0; i < n; ++i) 68 | A[i] = B[i] = tmp[i] = d(e); 69 | printf("start....\n"); 70 | auto start = clock(); 71 | sort(tmp, tmp + n); 72 | int sz = unique(tmp, tmp + n) - tmp; 73 | for (int i = 0; i < n; ++i) 74 | A[i] = lower_bound(tmp, tmp + sz, A[i]) - tmp; 75 | printf("std::sort: %f\n", static_cast(clock() - start) / CLOCKS_PER_SEC); 76 | start = clock(); 77 | Discretization_LLong::discretization(B, n); 78 | printf("std::sort: %f\n", static_cast(clock() - start) / CLOCKS_PER_SEC); 79 | for (int i = 0; i < n; ++i) 80 | if (A[i] != B[i]) 81 | abort(); 82 | return 0; 83 | } -------------------------------------------------------------------------------- /算法/基础算法/排列组合枚举.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | void enumerate(int n, int k) { 10 | //枚举[0, n)的所有的子集的子集 11 | for (int s = 0; s < (1 << n); ++s) { 12 | for (int s0 = s; s0; s0 = (s0 - 1) & s) { //枚举集合s的所有子集 13 | 14 | } 15 | } 16 | //枚举[0, n)中所有大小为k的子集 17 | for (int x, y, s = (1 << k) - 1; s < (1 << n); x = s & -s, y = s + x, s = (((s & ~y) / x) >> 1) | y) { 18 | //cout << bitset<5>(s) << endl; 19 | } 20 | //枚举[0, n)中所有大小为k的排列 21 | for (int x, y, s = (1 << k) - 1; s < (1 << n); x = s & -s, y = s + x, s = (((s & ~y) / x) >> 1) | y) { 22 | vector P; 23 | for (int i = 0; i < n; ++i) if (s & (1 << i)) 24 | P.push_back(i); 25 | do { 26 | //for (int i = 0; i < k; ++i) printf("%d ", P[i]); printf("\n"); 27 | } while (next_permutation(P.begin(), P.end())); 28 | } 29 | } 30 | int main() { 31 | enumerate(4, 3); 32 | return 0; 33 | } -------------------------------------------------------------------------------- /算法/基础算法/计时器.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | struct Timer { 5 | std::chrono::steady_clock::time_point start; 6 | Timer() : start(std::chrono::steady_clock::now()) {} 7 | ~Timer() { 8 | auto finish = std::chrono::steady_clock::now(); 9 | auto runtime = std::chrono::duration_cast(finish - start).count(); 10 | std::cerr << runtime / 1e6 << "s" << std::endl; 11 | } 12 | }; 13 | int main() { 14 | Timer timer; 15 | std::set S; 16 | for (int i = 0; i < 1e6; ++i) { 17 | S.insert(i); 18 | } 19 | return 0; 20 | } -------------------------------------------------------------------------------- /算法/字符串算法/AC自动机.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/字符串算法/AC自动机.cpp -------------------------------------------------------------------------------- /算法/字符串算法/KMP算法.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/字符串算法/KMP算法.cpp -------------------------------------------------------------------------------- /算法/字符串算法/LCT维护隐式后缀树.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 在每次调用extend函数构建后缀自动机的时候,extend函数会logn次调用solve函数, 3 | 这些次调用的[L, R]构成了连续区间[1, index]。solve函数的参数意思是对于以下标index结尾的字符串, 4 | 长度区间从[L, R]的这些子串上一次出现的位置都是x(x == -1表示之前没有出现过)。 5 | 可以通过在LCT上多维护一个标记,来维护上上次出现的位置。 6 | 对于询问母串中一个区间子串的问题,可以考虑用这个模型来解决。每次extend(w, index)的时候, 7 | 处理所有下标以index结尾的询问,并且动态维护左端点的询问值。 8 | 当前以index结尾的询问应该以index - 1的询问为基础,通过solve函数更新产生。 9 | solve函数每次会对一个左端点区间的询问值进行更新,可以考虑用线段树维护。 10 | */ 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | using namespace std; 18 | const int maxn = 210000; //maxn应当大于最大字符串长度的2倍 19 | int par[maxn], len[maxn], go[maxn][26]; 20 | int ch[maxn][2], fa[maxn], stk[maxn], pos[maxn]; 21 | int cur, root, last; 22 | long long ans = 0, base = 0; 23 | void solve(int L, int R, int x, int index) { 24 | //当前算法对于一个字符串中的所有子串 求出了其本质不同的子串个数 的和。 25 | if (x == -1) { 26 | int t = R - L + 1; 27 | base += t * (t + 1) / 2; 28 | } 29 | else { 30 | base += (index - x) * (R - L + 1); 31 | } 32 | } 33 | inline bool son(int x) { 34 | return ch[fa[x]][1] == x; 35 | } 36 | inline bool isroot(int x) { 37 | return ch[fa[x]][1] != x && ch[fa[x]][0] != x; 38 | } 39 | inline void pushdown(int x) { 40 | pos[ch[x][0]] = pos[ch[x][1]] = pos[x]; 41 | } 42 | void rotate(int x) { 43 | int y = fa[x], z = fa[y], c = son(x); 44 | if (!isroot(y)) 45 | ch[z][son(y)] = x; 46 | fa[x] = z; 47 | ch[y][c] = ch[x][!c]; 48 | fa[ch[y][c]] = y; 49 | ch[x][!c] = y; 50 | fa[y] = x; 51 | } 52 | void splay(int x) { 53 | int top = 0; 54 | stk[++top] = x; 55 | for (int i = x; !isroot(i); i = fa[i]) 56 | stk[++top] = fa[i]; 57 | while (top) 58 | pushdown(stk[top--]); 59 | for (int y = fa[x]; !isroot(x); rotate(x), y = fa[x]) if (!isroot(y)) 60 | son(x) ^ son(y) ? rotate(x) : rotate(y); 61 | } 62 | void access(int nd, int index) { 63 | for (int y = nd, x = fa[nd]; y > 1; y = x, x = fa[x]) { 64 | if (x) { 65 | splay(x); 66 | ch[x][1] = y; 67 | } 68 | solve(len[x] + 1, len[y], pos[y], index); 69 | } 70 | } 71 | void cut(int x) { //断开结点x与它的父结点之间的边 72 | splay(x); 73 | fa[ch[x][0]] = fa[x]; 74 | fa[x] = ch[x][0] = 0; 75 | } 76 | int newstate(int length) { 77 | len[cur] = length; 78 | return cur++; 79 | } 80 | void init() { 81 | memset(ch, 0, sizeof(ch)); 82 | memset(fa, 0, sizeof(fa)); 83 | memset(go, 0, sizeof(go)); 84 | memset(pos, -1, sizeof(pos)); 85 | cur = 1; 86 | root = last = newstate(0); 87 | } 88 | void extend(int w, int index) { 89 | int x = last; 90 | int nx = newstate(len[x] + 1); 91 | while (x && !go[x][w]) 92 | go[x][w] = nx, x = par[x]; 93 | if (!x) 94 | par[nx] = root, fa[nx] = root; 95 | else { 96 | int y = go[x][w]; 97 | if (len[x] + 1 == len[y]) 98 | par[nx] = y, fa[nx] = y; 99 | else { 100 | int ny = newstate(len[x] + 1); 101 | memcpy(go[ny], go[y], sizeof(go[y])); 102 | fa[ny] = par[y], par[ny] = par[y]; 103 | cut(y); 104 | pos[ny] = pos[y]; 105 | fa[y] = ny, par[y] = ny; 106 | fa[nx] = ny, par[nx] = ny; 107 | while (x && go[x][w] == y) 108 | go[x][w] = ny, x = par[x]; 109 | } 110 | } 111 | last = nx; 112 | access(last, index); 113 | splay(last); 114 | pos[last] = index; 115 | } 116 | 117 | char A[maxn]; 118 | int main() { 119 | freopen("in.txt", "r", stdin); 120 | init(); 121 | scanf("%s", A); 122 | int n = strlen(A); 123 | for (int i = 0; i < n; ++i) { 124 | extend(A[i] - 'A', i); 125 | ans += base; 126 | } 127 | printf("ans: %lld\n", ans); 128 | return 0; 129 | } -------------------------------------------------------------------------------- /算法/字符串算法/Lyndon分解.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | //Lyndon分解将一个字符串分解为若干个连续的子串S1, S2, ..., Sn,并且满足 9 | //S1 >= S2 >= ... >= Sn,并且所有这些子串都满足最小后缀是其本身。 10 | //该函数返回Lyndon分解成的所有子串的起始下标。 11 | vector Lyndon(const char* s) { 12 | int n = strlen(s); 13 | vector res; 14 | for (int i = 0; i < n; ) { 15 | int j = i, k = i + 1; 16 | while (k < n && s[j] <= s[k]) 17 | j = (s[j] == s[k++] ? j + 1 : i); 18 | while (i <= j) { 19 | res.push_back(i); 20 | i += k - j; 21 | } 22 | } 23 | return res; 24 | } 25 | const int maxn = 1110000; 26 | int pos[maxn]; //pos[i]表示前缀s[0, i]的最小后缀的下标 27 | void preprocess(const char* s) { 28 | int n = strlen(s); 29 | for (int i = 0; i < n; ) { 30 | int j = i, k = i + 1; 31 | pos[i] = i; 32 | while (k < n && s[j] <= s[k]) { 33 | if (s[j] < s[k]) { 34 | pos[k] = j = i; 35 | } 36 | else { 37 | pos[k] = pos[j] + k - j; 38 | j += 1; 39 | } 40 | k += 1; 41 | } 42 | while (i <= j) 43 | i += k - j; 44 | } 45 | } 46 | char s[maxn]; 47 | int main() { //hdu6761 48 | const int mod = 1e9 + 7; 49 | int T; 50 | scanf("%d", &T); 51 | while (T--) { 52 | scanf("%s", s); 53 | preprocess(s); 54 | int n = strlen(s); 55 | long long ans = 0, val = 1; 56 | for (int i = 0; i < n; ++i) { 57 | ans = (ans + (pos[i] + 1) * val) % mod; 58 | val = val * 1112 % mod; 59 | } 60 | printf("%lld\n", ans); 61 | } 62 | return 0; 63 | } -------------------------------------------------------------------------------- /算法/字符串算法/Manacher算法.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | const int maxn = 1.1e7 * 2 + 100; //maxn应当大于 原字符串长度的两倍加二 8 | char s[maxn]; 9 | int len[maxn]; 10 | //回文串在原字符串中的长度为len[i] - 1 11 | void manacher(char* str) { 12 | int n = strlen(str), m = 0; 13 | for (int i = 0; i < n; ++i) { 14 | s[++m] = '#'; 15 | s[++m] = str[i]; 16 | } 17 | s[++m] = '#'; s[0] = '$'; //s是加入新字符后的字符串 18 | len[1] = 1; 19 | int r = 1, po = 1; //r是当前极长回文子串的最右的端点 po为r对应的回文子串的中心 20 | for (int i = 1; i <= m; ++i) { 21 | if (i < r) 22 | len[i] = min(len[2 * po - i], r - i + 1); //2*po-i为i在当前这个极长回文子串中在左边相对应的位置 23 | else 24 | len[i] = 1; 25 | while (s[i + len[i]] == s[i - len[i]]) 26 | ++len[i]; 27 | if (i + len[i] > r) { 28 | r = i + len[i] - 1; 29 | po = i; 30 | } 31 | } 32 | //原字符串的最长回文子串为 max{ len[i] - 1 } 33 | //以i为中心的奇回文串的长度为len[2 * i + 2] - 1 34 | //以i为左中心的偶回文串的长度为len[2 * i + 3] - 1 35 | } 36 | char a[maxn / 2]; 37 | int main() { 38 | scanf("%s", a); 39 | const int n = strlen(a); 40 | manacher(a); 41 | int ans = *max_element(len, len + n * 2 + 4) - 1; 42 | printf("%d\n", ans); 43 | return 0; 44 | } -------------------------------------------------------------------------------- /算法/字符串算法/hash.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/字符串算法/hash.cpp -------------------------------------------------------------------------------- /算法/字符串算法/双端回文自动机.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | len[i]表示编号为i的节点表示的回文串的长度(一个节点表示一个回文串) 3 | fail[i]表示结点i失配以后跳转到的最长后缀回文串对应的结点 4 | cnt[i]表示结点i对应的字符串出现的次数(建树时求出的不是完全的,最后calc()函数跑一遍以后才是正确的) 5 | num[i]表示以结点i表示的最长回文串的最右端点为回文串结尾的回文串个数 6 | num[i]也表示结点i在fail树中的深度 7 | 若要判断回文自动机中是否包含回文串P[0, m - 1],则根据P的长度是奇数还是偶数来选择根结点(长度为偶数的回文串以0为根结点,长度为奇数的回文串以1为根结点), 8 | 然后将字符串P[m / 2, m - 1]输入自动机,到达了结点node,自动机包含回文串P当且仅当len[node] == m。 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | using namespace std; 16 | const int maxn = 300010; //maxn至少为字符串最大可能长度的两倍 17 | long long ans = 0; //统计当前的字符串中有多少个回文串(位置不同即不同) 18 | struct Palindromic_Tree { 19 | int ch[maxn][26], fail[maxn], info[maxn]; 20 | int cnt[maxn], num[maxn], len[maxn]; 21 | int last[2], cur, L, R; 22 | void init() { 23 | memset(ch, 0, sizeof(ch)); 24 | memset(cnt, 0, sizeof(cnt)); 25 | memset(info, -1, sizeof(info)); //info的初始值为不会出现在输入字符串中的值即可 26 | len[1] = -1; 27 | cur = fail[0] = last[1] = 1; 28 | last[0] = 0; 29 | L = maxn / 2; 30 | R = L - 1; 31 | } 32 | int get_fail(int x, int back) { 33 | int k = back ? R : L; 34 | int tp = back ? 1 : -1; 35 | while (info[k - (len[x] + 1) * tp] != info[k]) 36 | x = fail[x]; 37 | return x; 38 | } 39 | void insert(int w, int back) { //back为1则插入w到字符串的结尾,为0则插入到开头。 40 | if (back) info[++R] = w; 41 | else info[--L] = w; 42 | int p = get_fail(last[back], back); 43 | if (!ch[p][w]) { 44 | int u = ++cur; 45 | fail[u] = ch[get_fail(fail[p], back)][w]; 46 | len[u] = len[p] + 2; 47 | ch[p][w] = u; 48 | num[u] = num[fail[u]] + 1; 49 | } 50 | last[back] = ch[p][w]; 51 | if (len[last[back]] == R - L + 1) 52 | last[back ^ 1] = last[back]; 53 | ans += num[last[back]]; //当前结点的插入会使得字符串中新增num[last[back]]个新的回文串 54 | cnt[last[back]]++; 55 | } 56 | void calc() { 57 | for (int i = cur; i >= 2; --i) 58 | cnt[fail[i]] += cnt[i]; 59 | } 60 | }tree; 61 | int main(){ 62 | //freopen("in.txt", "r", stdin); 63 | int n; 64 | while (scanf("%d", &n) == 1) { 65 | tree.init(); ans = 0; 66 | while (n--) { 67 | int tp; 68 | scanf("%d", &tp); 69 | if (tp == 1) { 70 | char c; 71 | scanf(" %c", &c); 72 | tree.insert(c - 'a', false); 73 | } 74 | else if (tp == 2) { 75 | char c; 76 | scanf(" %c", &c); 77 | tree.insert(c - 'a', true); 78 | } 79 | else if (tp == 3) { 80 | printf("%d\n", tree.cur - 1); 81 | } 82 | else { 83 | printf("%lld\n", ans); 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /算法/字符串算法/后缀平衡树.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | const int maxn = 1110000; 9 | const double alpha = 0.6; 10 | struct SuffixBalancedTree { 11 | string data; 12 | double val[maxn], left, right; 13 | int sz[maxn], ch[maxn][2], root; 14 | int* pos, length, A[maxn]; 15 | void init() { 16 | data.resize(1); 17 | root = 0; 18 | } 19 | void pushup(int x) { 20 | sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1; 21 | } 22 | void dfs(int x) { 23 | if (!x) return; 24 | dfs(ch[x][0]); 25 | A[++length] = x; 26 | dfs(ch[x][1]); 27 | } 28 | int build(int a, int b, double L, double R) { 29 | if (a > b) return 0; 30 | int mid = (a + b) >> 1; 31 | int x = A[mid]; 32 | sz[x] = b - a + 1; 33 | val[x] = (L + R) / 2; 34 | ch[x][0] = build(a, mid - 1, L, val[x]); 35 | ch[x][1] = build(mid + 1, b, val[x], R); 36 | return x; 37 | } 38 | void add(int& x, double L, double R) { 39 | if (!x) { 40 | x = data.size() - 1; 41 | ch[x][0] = ch[x][1] = 0; 42 | val[x] = (L + R) / 2; 43 | sz[x] = 1; 44 | return; 45 | } 46 | int y = data.size() - 1; 47 | if (data[y] < data[x] || data[y] == data[x] && val[y - 1] < val[x - 1]) 48 | add(ch[x][0], L, val[x]); 49 | else 50 | add(ch[x][1], val[x], R); 51 | pushup(x); 52 | if (sz[ch[x][0]] > sz[x] * alpha || sz[ch[x][1]] > sz[x] * alpha) 53 | pos = &x, left = L, right = R; 54 | } 55 | void push_front(char c) { //向开头添加一个新的字符之后会改变其他位置的下标 56 | data.push_back(c); 57 | pos = nullptr; 58 | add(root, 0, 1); 59 | if (pos) { 60 | length = 0; 61 | dfs(*pos); 62 | *pos = build(1, length, left, right); 63 | } 64 | } 65 | int merge(int x, int y) { 66 | if (!x || !y) 67 | return x | y; 68 | if (sz[x] > sz[y]) { 69 | ch[x][1] = merge(ch[x][1], y); 70 | pushup(x); 71 | return x; 72 | } 73 | else { 74 | ch[y][0] = merge(x, ch[y][0]); 75 | pushup(y); 76 | return y; 77 | } 78 | } 79 | void del(int& x) { 80 | const int y = data.size() - 1; 81 | sz[x] -= 1; 82 | if (x == y) 83 | x = merge(ch[x][0], ch[x][1]); 84 | else if (val[y] < val[x]) 85 | del(ch[x][0]); 86 | else 87 | del(ch[x][1]); 88 | } 89 | void pop_front() { 90 | del(root); 91 | data.pop_back(); 92 | } 93 | double weight(int index) { //返回下标index的权值,权值越小,字典序越小(下标从1开始) 94 | return val[data.size() - index]; 95 | } 96 | int rank(int index) { //返回下标index的排名(下标从1开始) 97 | double key = weight(index); 98 | int ret = 0, x = root; 99 | while (x) { 100 | if (key < val[x]) 101 | x = ch[x][0]; 102 | else 103 | ret += sz[ch[x][0]] + 1, x = ch[x][1]; 104 | } 105 | return ret; 106 | } 107 | int rank(const char *s) { //返回字典序小于s的后缀的个数 108 | int ret = 0, x = root, n = strlen(s); 109 | while (x) { 110 | int L = min(x, n) + 1; 111 | int flag = 0; 112 | for (int i = 0; i < L; ++i) { 113 | if (s[i] != data[x - i]) { 114 | flag = s[i] - data[x - i]; 115 | break; 116 | } 117 | } 118 | if (flag <= 0) 119 | x = ch[x][0]; 120 | else 121 | ret += sz[ch[x][0]] + 1, x = ch[x][1]; 122 | } 123 | return ret; 124 | } 125 | }tree; 126 | int main() { 127 | 128 | return 0; 129 | } -------------------------------------------------------------------------------- /算法/字符串算法/回文串Border.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 定义dif[x] = len[x] - len[fail[x]], 3 | slink[x]为x后缀链接路径上第一个dif[x]≠dif[fail[x]]的祖先。 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | const int maxn = 110000; 12 | const int sigma_size = 26; 13 | int info[maxn], ch[maxn][sigma_size], fail[maxn], len[maxn], dif[maxn], slink[maxn]; 14 | int node[maxn], last, cur, sz; 15 | void init() { 16 | memset(ch, 0, sizeof(ch)); 17 | fail[0] = 1; 18 | info[0] = -1; 19 | len[1] = -1; 20 | cur = last = 1; 21 | sz = 0; 22 | } 23 | void extend(int w) { 24 | int p = last; 25 | info[++sz] = w; 26 | while (info[sz - len[p] - 1] != w) 27 | p = fail[p]; 28 | if (!ch[p][w]) { 29 | int u = ++cur, x = fail[p]; 30 | while (info[sz - len[x] - 1] != w) 31 | x = fail[x]; 32 | len[u] = len[p] + 2; 33 | fail[u] = ch[x][w]; //(*) 34 | ch[p][w] = u; 35 | dif[u] = len[u] - len[fail[u]]; 36 | if (dif[u] != dif[fail[u]]) 37 | slink[u] = fail[u]; 38 | else 39 | slink[u] = slink[fail[u]]; 40 | } 41 | last = ch[p][w]; 42 | } 43 | void insert(char* str) { //输入字符串下标从0开始 44 | int n = strlen(str); 45 | for (int i = 0; i < n; ++i) { 46 | extend(str[i] - 'a'); 47 | node[i] = last; 48 | } 49 | } 50 | int main() { 51 | 52 | return 0; 53 | } -------------------------------------------------------------------------------- /算法/字符串算法/回文自动机.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 1. len[i]表示编号为i的节点对应的回文串的长度(一个节点表示一个回文串)。 3 | 2. fail[i]表示结点i失配以后跳转到的最长后缀回文串对应的结点。 4 | 3. par[i]表示结点i删除掉最外层的一个字符后得到的回文串对应的结点。 5 | 例如结点i表示的回文串为cabbac,则结点par[i]对应的回文串为abba。 6 | 4. node[i]表示输入字符串中以下标i结尾的最长回文串对应的回文树结点。 7 | 5. cnt[i]表示结点i对应的字符串出现的次数(建树时求出的不是完全的,最后calc()函数跑一遍以后才是正确的)。 8 | 6. num[i]表示 以结点i表示的最长回文串的最右端点 为回文串结尾的回文串个数。 9 | 7. 若要判断回文自动机中是否包含回文串P[0, m - 1],则根据P的长度是奇数还是偶数来选择根结点(长度为偶数的回文串以0为根结点,长度为奇数的回文串以1为根结点), 10 | 然后将字符串P[m / 2, m - 1]输入自动机,到达了结点node,自动机包含回文串P当且仅当len[node] == m。 11 | 8. 一般来说不需要对回文自动机进行dfs,因为循环for (int i = 2; i <= cur; ++i)就可以按照fail树的拓扑序 12 | (也是par树的拓扑序)访问结点。 13 | */ 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | using namespace std; 20 | const int maxn = 110000; 21 | const int sigma_size = 26; 22 | int info[maxn], ch[maxn][sigma_size], fail[maxn], len[maxn]; 23 | int cnt[maxn], num[maxn], par[maxn], node[maxn], last, cur, sz; 24 | void init() { 25 | memset(ch, 0, sizeof(ch)); 26 | memset(cnt, 0, sizeof(cnt)); 27 | fail[0] = 1; 28 | info[0] = -1; 29 | len[1] = -1; 30 | cur = last = 1; 31 | sz = 0; 32 | } 33 | void extend(int w) { 34 | int p = last; 35 | info[++sz] = w; 36 | while (info[sz - len[p] - 1] != w) 37 | p = fail[p]; 38 | if (!ch[p][w]) { 39 | int u = ++cur, x = fail[p]; 40 | while (info[sz - len[x] - 1] != w) 41 | x = fail[x]; 42 | par[u] = p; 43 | len[u] = len[p] + 2; 44 | fail[u] = ch[x][w]; //(*) 45 | ch[p][w] = u; 46 | num[u] = num[fail[u]] + 1; 47 | } 48 | last = ch[p][w]; 49 | cnt[last]++; 50 | } 51 | void insert(char* str) { //输入字符串下标从0开始 52 | int n = strlen(str); 53 | for (int i = 0; i < n; ++i) { 54 | extend(str[i] - 'a'); 55 | node[i] = last; 56 | } 57 | } 58 | void calc() { 59 | //更新fail的地方只有(*)处,此时将新建的结点u连接到之前的结点上而不改变之前的结点的连接状态, 60 | //这样,对于从cur到2的循环,就是对fail树按拓扑序逆序循环(也是对par树按拓扑序逆序循环)。 61 | for (int i = cur; i >= 2; --i) 62 | cnt[fail[i]] += cnt[i]; 63 | } 64 | int main() { 65 | int A[] = { 1, 2, 3, 2, 1, 3, 3 }, B[] = { 3, 2, 1 }; 66 | init(); 67 | for (int i = 0; i < sizeof(A) / sizeof(*A); ++i) 68 | extend(A[i]); 69 | int st = 0; //0偶数,1奇数 70 | for (int i = 0; i < sizeof(B) / sizeof(*B); ++i) 71 | st = ch[st][B[i]]; 72 | printf("%d\n", len[st]); 73 | return 0; 74 | } -------------------------------------------------------------------------------- /算法/字符串算法/基于后缀自动机构建后缀树.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | const int maxn = 2010000; 8 | int par[maxn], len[maxn], cur, root, last; //par[x]是后缀树上x的父结点 9 | int ch[maxn][26]; //ch[x][w]表示结点x在后缀树中沿着字符w走到达的结点。 10 | bool issuf[maxn]; //issuf[x]记录结点x是否表示一个后缀,若是则pos[x]就是后缀的下标。 11 | int pos[maxn]; //pos[x]表示结点x对应的字符串在原串中的起始下标。 12 | int node[maxn]; //node[i]表示下标i开始的后缀在树中的结点编号。 13 | int newstate(int length, int index) { 14 | len[cur] = length; 15 | pos[cur] = index; 16 | return cur++; 17 | } 18 | void init() { 19 | memset(ch, 0, sizeof(ch)); 20 | cur = 1; 21 | root = last = newstate(0, -1); 22 | } 23 | void extend(int w, int index) { 24 | int x = last; 25 | int nx = newstate(len[x] + 1, index); 26 | node[index] = nx; 27 | issuf[nx] = true; 28 | while (x && !ch[x][w]) 29 | ch[x][w] = nx, x = par[x]; 30 | if (!x) 31 | par[nx] = root; 32 | else { 33 | int y = ch[x][w]; 34 | if (len[x] + 1 == len[y]) 35 | par[nx] = y; 36 | else { 37 | int ny = newstate(len[x] + 1, pos[y]); 38 | memcpy(ch[ny], ch[y], sizeof(ch[y])); 39 | par[ny] = par[y]; 40 | par[nx] = par[y] = ny; 41 | while (x && ch[x][w] == y) 42 | ch[x][w] = ny, x = par[x]; 43 | } 44 | } 45 | last = nx; 46 | } 47 | void build(const char* s) { 48 | int n = strlen(s); 49 | for (int i = n - 1; i >= 0; --i) 50 | extend(s[i] - 'a', i); 51 | memset(ch, 0, sizeof(ch[0]) * (cur + 10)); 52 | for (int i = 2; i < cur; ++i) { 53 | int w = s[pos[i] + len[par[i]]] - 'a'; 54 | ch[par[i]][w] = i; 55 | } 56 | } 57 | 58 | const int maxlog = 23; 59 | int anc[maxn][maxlog]; //anc[i][j]表示结点i往上走2^j个点到达的点 60 | void preprocess() { //后缀树上倍增初始化 61 | for (int i = 1; i < cur; ++i) { 62 | anc[i][0] = par[i]; 63 | for (int j = 1; (1 << (j - 1)) < cur; ++j) 64 | anc[i][j] = 0; 65 | } 66 | for (int j = 1; (1 << j) < cur; ++j) { 67 | for (int i = 1; i < cur; ++i) { 68 | if (anc[i][j - 1] != 0) { 69 | int a = anc[i][j - 1]; 70 | anc[i][j] = anc[a][j - 1]; 71 | } 72 | } 73 | } 74 | } 75 | int query(int index, int length) { //从下标index的后缀往上走到len[p] >= length的深度最小的结点 76 | int p = node[index]; //若要识别字符串的子串[L, R],则调用query(L, R-L+1) 77 | for (int i = maxlog - 1; i >= 0; --i) 78 | if (len[anc[p][i]] >= length) 79 | p = anc[p][i]; 80 | return p; 81 | } 82 | 83 | int main() { 84 | 85 | return 0; 86 | } -------------------------------------------------------------------------------- /算法/字符串算法/序列自动机.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/字符串算法/序列自动机.cpp -------------------------------------------------------------------------------- /算法/字符串算法/扩展KMP.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/字符串算法/扩展KMP.cpp -------------------------------------------------------------------------------- /算法/字符串算法/非势能分析回文自动机.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 每次插入时间复杂度都为O(1)的回文自动机(不基于势能分析),可用于回滚莫队。 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | const int maxn = 300007; 12 | struct Palindromic_Tree { 13 | int cur, cnt[maxn], fail[maxn], ch[maxn][26], len[maxn], quick[maxn][26]; 14 | int last[2], info[maxn * 2], L, R; 15 | void init() { 16 | fail[0] = cur = 1; 17 | len[1] = -1; 18 | last[0] = last[1] = 0; 19 | L = maxn; 20 | R = L - 1; 21 | memset(ch, 0, sizeof(ch)); //多组数据时应当在新建结点的时候清空 22 | memset(cnt, 0, sizeof(cnt)); 23 | memset(info, -1, sizeof(info)); 24 | for (int i = 0; i < 26; i++) 25 | quick[1][i] = quick[0][i] = 1; 26 | } 27 | void extend(int w, int back = 1) { //back为1则插入w到字符串的结尾,为0则插入到开头。 28 | int tp = back ? -1 : 1; 29 | int x = back ? ++R : --L; 30 | int p = last[back]; 31 | info[x] = w; 32 | if (info[x + tp * (len[p] + 1)] != info[x]) 33 | p = quick[p][w]; 34 | if (!ch[p][w]) { 35 | int u = ++cur; 36 | len[u] = len[p] + 2; 37 | int now = fail[p]; 38 | if (info[x + tp * (len[now] + 1)] != info[x]) 39 | now = quick[now][w]; 40 | fail[u] = ch[now][w]; 41 | memcpy(quick[u], quick[fail[u]], sizeof(quick[u])); 42 | quick[u][info[x + tp * len[fail[u]]]] = fail[u]; 43 | ch[p][w] = u; 44 | } 45 | last[back] = ch[p][w]; 46 | if (len[last[back]] == R - L + 1) 47 | last[back ^ 1] = last[back];//注意这里 48 | cnt[last[back]]++; 49 | } 50 | } tree; 51 | namespace Palindromic_Tree_Back { 52 | int info[maxn], ch[maxn][26], quick[maxn][26], fail[maxn], len[maxn]; 53 | int last, cur, sz, n, m; 54 | vector node; 55 | void init() { 56 | node.clear(); 57 | memset(ch, 0, sizeof(ch[0]) * 5); 58 | fail[0] = 1; 59 | info[0] = len[1] = -1; 60 | cur = last = 1; 61 | sz = 0; 62 | for (int i = 0; i < 26; ++i) 63 | quick[1][i] = quick[0][i] = 1; 64 | } 65 | void extend(int w) { // 0 <= w <= sigma_size 66 | int p = last; 67 | info[++sz] = w; 68 | if (info[sz - len[p] - 1] != w) 69 | p = quick[p][w]; 70 | if (!ch[p][w]) { 71 | int u = ++cur, x = fail[p]; 72 | memset(ch[u], 0, sizeof(ch[u])); 73 | if (info[sz - len[x] - 1] != w) 74 | x = quick[x][w]; 75 | len[u] = len[p] + 2; 76 | fail[u] = ch[x][w]; 77 | ch[p][w] = u; 78 | memcpy(quick[u], quick[fail[u]], sizeof(quick[u])); 79 | quick[u][info[sz - len[fail[u]]]] = fail[u]; 80 | //value[u] = (value[fail[u]] + (n - len[u] + 1) * pow_mod(26, n - len[u])) % mod; 81 | } 82 | last = ch[p][w]; 83 | //answer = (answer + value[last]) % mod; 84 | } 85 | void rollback() { //回滚 86 | int x = node.back(); 87 | node.pop_back(); 88 | last = node.back(); 89 | sz -= 1; 90 | //answer = (answer - value[x] + mod) % mod; 91 | } 92 | } 93 | 94 | char A[maxn]; 95 | int c[maxn], id[maxn]; 96 | int main() { 97 | scanf("%s", A + 1); 98 | int L = strlen(A + 1); 99 | tree.init(); 100 | long long ans = 0; 101 | int T = L >> 1; 102 | for (int i = T + 1; i <= L; i++) tree.extend(A[i] - 'a', 1); 103 | for (int i = T; i; i--) tree.extend(A[i] - 'a', 0); 104 | for (int i = 2; i <= tree.cur; i++) c[tree.len[i]]++; 105 | for (int i = 1; i <= L; i++) c[i] += c[i - 1]; 106 | for (int i = 2; i <= tree.cur; i++) id[c[tree.len[i]]--] = i; 107 | for (int i = tree.cur - 1; i; i--) tree.cnt[tree.fail[id[i]]] += tree.cnt[id[i]]; 108 | for (int i = 2; i <= tree.cur; i++) ans = max(ans, 1ll * tree.len[i] * tree.cnt[i]); 109 | printf("%lld\n", ans); 110 | } -------------------------------------------------------------------------------- /算法/数学算法/QR迭代.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | const int maxn = 111; 10 | const double eps = 1e-10; 11 | using matrix = double[maxn][maxn]; 12 | matrix Q, R; 13 | double u[maxn], w[maxn]; 14 | vector eigenvalues(matrix& A, int n) { 15 | for (int T = 0; T < 1000000; ++T) { 16 | for (int i = 0; i < n; ++i) { 17 | for (int j = 0; j < n; ++j) { 18 | Q[i][j] = 0; 19 | R[i][j] = A[i][j]; 20 | } 21 | Q[i][i] = 1; 22 | } 23 | for (int i = 0; i < n; ++i) { 24 | double sum = 0; 25 | for (int j = i; j < n; ++j) 26 | sum += fabs(R[i][j]); 27 | if (sum < eps) 28 | continue; 29 | double sigma = R[i][i] <= 0 ? 1 : -1; 30 | double tot = 0; 31 | for (int j = i; j < n; ++j) { 32 | tot += R[j][i] * R[j][i]; 33 | w[j] = -sigma * R[j][i]; 34 | } 35 | w[i] += sqrt(tot); 36 | tot = sqrt(tot - R[i][i] * R[i][i] + w[i] * w[i]); 37 | for (int j = i; j < n; ++j) 38 | u[j] = w[j] / tot; 39 | for (int j = 0; j < n; ++j) { 40 | double product = 0; 41 | for (int k = i; k < n; ++k) 42 | product += u[k] * Q[k][j]; 43 | for (int k = i; k < n; ++k) 44 | Q[k][j] = sigma * (Q[k][j] - 2 * product * u[k]); 45 | } 46 | for (int j = i; j < n; ++j) { 47 | double product = 0; 48 | for (int k = i; k < n; ++k) 49 | product += u[k] * R[k][j]; 50 | for (int k = i; k < n; ++k) 51 | R[k][j] = sigma * (R[k][j] - 2 * product * u[k]); 52 | } 53 | } 54 | for (int i = 0; i < n; ++i) { 55 | for (int j = 0; j < n; ++j) { 56 | A[i][j] = 0; 57 | for (int k = 0; k < n; ++k) 58 | A[i][j] += R[i][k] * Q[j][k]; 59 | } 60 | } 61 | } 62 | vector ret; 63 | for (int i = 0; i < n; ++i) 64 | ret.push_back(R[i][i]); 65 | for (int i = 0; i < n; ++i) { 66 | for (int j = 0; j < n; ++j) 67 | printf("%.10f ", R[i][j]); 68 | printf("\n"); 69 | } 70 | return ret; 71 | } 72 | matrix A = { {-1, 1, 0}, {-4, 3, 0 }, {1, 0, 2} }; 73 | int main() { 74 | eigenvalues(A, 3); 75 | return 0; 76 | } -------------------------------------------------------------------------------- /算法/数学算法/SG函数.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数学算法/SG函数.cpp -------------------------------------------------------------------------------- /算法/数学算法/三分求极值.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #define f(x) fabs(x - 8) 8 | using namespace std; 9 | const double eps = 1e-6; 10 | int maximum_int(int L, int R) { //三分求f函数的最大值(定义域为整数) 11 | while (R > L) { 12 | int m1 = (2 * L + R) / 3; 13 | int m2 = (2 * R + L + 2) / 3; 14 | if (f(m1) > f(m2)) 15 | R = m2 - 1; 16 | else 17 | L = m1 + 1; 18 | } 19 | return L; //f(L)为最大值 20 | } 21 | int minimun_int(int L, int R) { //三分求f函数的最小值(定义域为整数) 22 | while (R > L) { 23 | int m1 = (2 * L + R) / 3; 24 | int m2 = (2 * R + L + 2) / 3; 25 | if (f(m1) < f(m2)) 26 | R = m2 - 1; 27 | else 28 | L = m1 + 1; 29 | } 30 | return L; //f(L)为最小值 31 | } 32 | double maximum_double(double L, double R) { //三分求f函数的最大值(定义域为实数) 33 | while (R - L > eps) { // for i in range(100): 34 | double m1 = (2 * L + R) / 3; 35 | double m2 = (2 * R + L) / 3; 36 | if (f(m1) > f(m2)) 37 | R = m2; 38 | else 39 | L = m1; 40 | } 41 | return L; //f(L)为最大值 42 | } 43 | double minimun_double(double L, double R) { //三分求f函数的最小值(定义域为实数) 44 | while (R - L > eps) { // for i in range(100): 45 | double m1 = (2 * L + R) / 3; 46 | double m2 = (2 * R + L) / 3; 47 | if (f(m1) < f(m2)) 48 | R = m2; 49 | else 50 | L = m1; 51 | } 52 | return L; //f(L)为最小值 53 | } 54 | int main() { 55 | double pos = minimun_double(-1, 100); 56 | printf("%f\n", pos); 57 | return 0; 58 | } -------------------------------------------------------------------------------- /算法/数学算法/二元一次不定方程.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | void gcd(long long a, long long b, long long& d, long long& x, long long& y) { 9 | if (!b) { 10 | d = a; 11 | x = 1; 12 | y = 0; 13 | } 14 | else { 15 | gcd(b, a % b, d, y, x); 16 | y -= x * (a / b); 17 | } 18 | } 19 | int main() { 20 | //freopen("in.txt", "r", stdin); 21 | int T; 22 | scanf("%d", &T); 23 | while (T--) { 24 | long long a, b, c, g, x, y; 25 | scanf("%lld %lld %lld", &a, &b, &c); 26 | gcd(a, b, g, x, y); 27 | if (c % g != 0) { //ax + by = c 无解 28 | puts("-1"); 29 | continue; 30 | } 31 | x = x * c / g, y = y * c / g; //算出一组特解 32 | long long dx = b / g, dy = a / g; 33 | //通解的形式为 (x + s * dx, y - s * dy) 34 | //若要求x和y都大于0,则s的取值范围为[L, R] 35 | long long L = x > 0 ? (1 - x) / dx : -x / dx + 1; 36 | long long R = y > 0 ? (y - 1) / dy : y / dy - 1; 37 | if (L <= R) 38 | printf("%lld %lld %lld %lld %lld\n", R - L + 1, x + L * dx, y - R * dy, 39 | x + R * dx, y - L * dy); 40 | else //无正整数数解时,分别输出x和y的最小正整数值 41 | printf("%lld %lld\n", x + L * dx, y - R * dy); 42 | } 43 | return 0; 44 | } -------------------------------------------------------------------------------- /算法/数学算法/多项式拟合(泰勒展开).py: -------------------------------------------------------------------------------- 1 | #该程序展示了用15次多项式去拟合函数$f(x) = \int_0^xe^{-x^2}dx$ 2 | #这个积分是求不出来原函数的,但是我们可以将$e^{-x^2}$泰勒展开,然后再对得到的每个多项式积分就得到了一个和式: 3 | #$\sum_{n=0}^{\infty}\frac{(-1)^nx^{2n+1}}{n!(2n+1)}$ 4 | #这个和式就是f(x)的泰勒展开式,我们用其前50项作为对f(x)的近似,并求出拟合函数。 5 | from decimal import * 6 | getcontext().prec = 100 7 | maxn = 210 8 | b = Decimal(1) 9 | F = [0, 1] 10 | for i in range(1, 50): #F为待求函数的泰勒展开式的系数,F[i]为x^i的系数,F中的项越多近似越好 11 | b *= -i 12 | F.append(0) 13 | F.append(1 / b / Decimal(2 * i + 1)) 14 | def asme(pos): 15 | length = pos + len(F) + 2 16 | t = [Decimal(0) for i in range(length)] 17 | for i in range(pos + 1): 18 | for j in range(len(F)): 19 | t[i + j] += a[pos][i] * F[j] 20 | res = Decimal(0) 21 | for i in range(length): 22 | res += t[i] * (pr[i + 1] - pl[i + 1]) / (i + 1) 23 | return res 24 | L, R, T = Decimal(0), Decimal(2), 15 #待拟合区间为[L, R],所用多项式的最高次数为T 25 | a = [[Decimal(0) for j in range(maxn)] for i in range(maxn)] 26 | c = [Decimal(0) for i in range(maxn)] 27 | pl = [Decimal(1)] 28 | pr = [Decimal(1)] 29 | for i in range(maxn): 30 | pl.append(pl[-1] * L) 31 | pr.append(pr[-1] * R) 32 | a[0][0] = 1 / (R - L).sqrt() 33 | for i in range(1, T + 1): 34 | a[i][i] = Decimal(1) 35 | for j in range(i): 36 | sum = Decimal(0) 37 | for k in range(j + 1): 38 | sum += a[j][k] * (pr[i + k + 1] - pl[i + k + 1]) / (i + k + 1) 39 | for k in range(j + 1): 40 | a[i][k] -= sum * a[j][k] 41 | total = Decimal(0) 42 | for j in range(i + 1): 43 | for k in range(i + 1): 44 | total += a[i][j] * a[i][k] * (pr[j + k + 1] - pl[j + k + 1]) / (j + k + 1) 45 | length = total.sqrt() 46 | for j in range(i + 1): 47 | a[i][j] /= length 48 | for pos in range(T + 1): 49 | res = asme(pos); 50 | for i in range(pos + 1): 51 | c[i] += a[pos][i] * res; 52 | for i in range(T + 1): 53 | print("%.20e" % c[i], end=", ") 54 | -------------------------------------------------------------------------------- /算法/数学算法/多项式拟合(辛普森积分).cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | const int maxn = 110; 10 | const long double pi = acos(-1.0L); 11 | const long double L = -pi, R = pi; //函数f(x)的定义域为[L, R] 12 | const int T = 15; //用不超过T次的多项式对函数f(x)进行拟合 13 | long double a[maxn][maxn]; //a[i]表示第i个规范正交基,a[i][j]表示第i个基的x^j的系数 14 | long double pl[maxn], pr[maxn]; //pl[i] = pow(L, i), pr[i] = pow(R, i) 15 | long double c[maxn]; //拟合出来的多项式系数 16 | int pos = 0; 17 | long double f(long double x) { //待拟合函数 18 | return sin(x); 19 | } 20 | long double calc(long double x) { 21 | long double result = 0, y = 1; 22 | for (int i = 0; i <= pos; ++i) { 23 | result += a[pos][i] * y; 24 | y *= x; 25 | } 26 | return result * f(x); 27 | } 28 | long double simpson(long double l, long double r) { //计算函数calc在区间[l, r]上的辛普森积分 29 | return (calc(l) + calc(r) + 4 * calc((l + r) / 2)) * (r - l) / 6; 30 | } 31 | long double solve(long double l, long double r, long double eps, long double val) { //递归求解积分 32 | long double mid = (l + r) / 2; 33 | long double L = simpson(l, mid), R = simpson(mid, r); 34 | if (fabs(L + R - val) <= 15 * eps) 35 | return L + R + (L + R - val) / 15; 36 | return solve(l, mid, eps / 2, L) + solve(mid, r, eps / 2, R); 37 | } 38 | //eps设置太小可能会导致死循环 39 | long double asme(long double l, long double r, long double eps = 1e-16l) { //自适应辛普森积分 40 | return solve(l, r, eps, simpson(l, r)); //求函数calc在区间[l, r]上的自适应辛普森积分,eps指定精度 41 | } 42 | int main() { 43 | pl[0] = pr[0] = 1; 44 | for (int i = 1; i < maxn; ++i) { 45 | pl[i] = pl[i - 1] * L; 46 | pr[i] = pr[i - 1] * R; 47 | } 48 | a[0][0] = 1 / sqrt(R - L); 49 | for (int i = 1; i <= T; ++i) { //计算第i个规范正交基 50 | a[i][i] = 1; 51 | for (int j = 0; j < i; ++j) { 52 | long double sum = 0; //sum = 53 | for (int k = 0; k <= j; ++k) 54 | sum += a[j][k] * (pr[i + k + 1] - pl[i + k + 1]) / (i + k + 1); 55 | for (int k = 0; k <= j; ++k) 56 | a[i][k] -= sum * a[j][k]; 57 | } 58 | long double total = 0; 59 | for (int j = 0; j <= i; ++j) 60 | for (int k = 0; k <= i; ++k) 61 | total += a[i][j] * a[i][k] * (pr[j + k + 1] - pl[j + k + 1]) / (j + k + 1); 62 | long double length = sqrt(total); 63 | for (int j = 0; j <= i; ++j) 64 | a[i][j] /= length; 65 | } 66 | for (::pos = 0; pos <= T; ++pos) { 67 | long double res = asme(L, R); 68 | for (int i = 0; i <= pos; ++i) 69 | c[i] += a[pos][i] * res; 70 | } 71 | for (int i = 0; i <= T; ++i) 72 | printf("%.17le,", c[i]); 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /算法/数学算法/快速傅里叶变换.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | using namespace std; 10 | const int maxn = 1 << 22; //必须是2的幂 11 | const double pi = acos(-1); 12 | complex w[maxn]; 13 | void init() { 14 | for (int i = 0;i < maxn;++i) 15 | w[i] = { cos(pi * 2 * i / maxn), sin(pi * 2 * i / maxn) }; 16 | } 17 | void fft(vector> &a, bool inverse) { 18 | for (int i = 0, j = 0; i < a.size(); ++i) { 19 | if (i < j) 20 | std::swap(a[i], a[j]); 21 | for (int k = a.size() >> 1; (j ^= k) < k; k >>= 1); 22 | } 23 | for (int step = 2; step <= a.size(); step *= 2) { 24 | int h = step / 2, d = maxn / step; 25 | for (int i = 0; i < a.size(); i += step) 26 | for (int j = 0; j < h; ++j) { 27 | auto &x = a[i + j]; 28 | auto &y = a[i + j + h]; 29 | auto t = w[d * j] * y; 30 | y = x - t; 31 | x = x + t; 32 | } 33 | } 34 | if (inverse) { 35 | std::reverse(a.begin() + 1, a.end()); 36 | for (auto& x : a) 37 | x /= a.size(); 38 | } 39 | } 40 | vector operator* (const vector &a, const vector &b) { 41 | int size = 2; 42 | while (size < a.size() + b.size()) size <<= 1; 43 | vector> x(size), y(size); 44 | copy(a.begin(), a.end(), x.begin()); 45 | copy(b.begin(), b.end(), y.begin()); 46 | fft(x, false); 47 | fft(y, false); 48 | for (int i = 0; i < size; i++) 49 | x[i] *= y[i]; 50 | fft(x, true); 51 | vector res(a.size() + b.size() - 1); 52 | for (int i = 0; i < res.size(); ++i) 53 | res[i] = x[i].real(); 54 | return res; 55 | } 56 | int main() { 57 | int n, m; 58 | init(); 59 | scanf("%d %d", &n, &m); 60 | vector a(n + 1), b(m + 1); 61 | for (int i = 0; i <= n; ++i) 62 | scanf("%lf", &a[i]); 63 | for (int i = 0; i <= m; ++i) 64 | scanf("%lf", &b[i]); 65 | auto ans = a * b; 66 | for (int i = 0; i < ans.size(); ++i) 67 | printf("%.0f ", ans[i] + 1e-8); 68 | return 0; 69 | } -------------------------------------------------------------------------------- /算法/数学算法/快速幂运算.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数学算法/快速幂运算.cpp -------------------------------------------------------------------------------- /算法/数学算法/欧拉函数.cpp: -------------------------------------------------------------------------------- 1 | //phi(n) 表示小于n且与n互素的整数个数 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | const int maxn = 1000000; 9 | int vis[maxn], prime[maxn], phi[maxn], cnt; 10 | void init() { 11 | memset(vis, 0, sizeof(vis)); 12 | phi[1] = 1; 13 | cnt = 0; 14 | for (int i = 2; i < maxn; i++) { 15 | if (!vis[i]) { 16 | prime[cnt++] = i; 17 | phi[i] = i - 1; 18 | } 19 | for (int j = 0; j < cnt && i * prime[j] < maxn; j++) { 20 | int t = i * prime[j]; 21 | vis[t] = 1; 22 | if (i % prime[j] == 0) { 23 | phi[t] = phi[i] * prime[j]; 24 | break; 25 | } 26 | else { 27 | phi[t] = phi[i] * phi[prime[j]]; 28 | } 29 | } 30 | } 31 | } 32 | int euler(int n) { //时间复杂度O(sqrt(n)) 33 | int ans = n; 34 | for (int i = 2; i * i <= n; ++i) if (n % i == 0) { 35 | ans = ans / i * (i - 1); 36 | while (n % i == 0) 37 | n /= i; 38 | } 39 | if (n > 1) 40 | ans = ans / n * (n - 1); 41 | return ans; 42 | } 43 | int main() { 44 | init(); 45 | for (int i = 0; i <= 10000; ++i) if (phi[i] != euler(i)) 46 | printf("%d\n", i); 47 | return 0; 48 | } -------------------------------------------------------------------------------- /算法/数学算法/求解异或方程组.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | const int maxn = 110; 9 | bitset a[maxn]; 10 | void Gauss(int n) { 11 | int now = 0; //now记录当前正在处理的行号 12 | for (int i = 0; i < n; ++i) { //i记录当前正在处理的列号 13 | int j = now; 14 | while (j < n && !a[j][i]) ++j; 15 | if (j == n) continue; 16 | if (j != now) swap(a[now], a[j]); 17 | for (int k = 0; k < n; ++k) 18 | if (k != now && a[k][i]) 19 | a[k] ^= a[now]; 20 | ++now; 21 | } 22 | } 23 | int main() { 24 | 25 | return 0; 26 | } -------------------------------------------------------------------------------- /算法/数学算法/牛顿迭代求非线性方程组.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数学算法/牛顿迭代求非线性方程组.cpp -------------------------------------------------------------------------------- /算法/数学算法/矩阵与状态转移.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数学算法/矩阵与状态转移.cpp -------------------------------------------------------------------------------- /算法/数学算法/矩阵求逆.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | const int maxn = 555; 9 | namespace inverse_double { 10 | using matrix = double[maxn][maxn]; 11 | double temp[maxn][maxn * 2]; 12 | void inverse(const matrix& A, matrix& res, int n) { 13 | for (int i = 0; i < n; ++i) { 14 | for (int j = 0; j < n; ++j) 15 | temp[i][j] = A[i][j], temp[i][n + j] = 0; 16 | temp[i][n + i] = 1; 17 | } 18 | for (int i = 0; i < n; ++i) { 19 | int r = i; 20 | for (int j = i + 1; j < n; ++j) 21 | if (fabs(temp[j][i]) > fabs(temp[r][i])) 22 | r = j; 23 | if (r != i) 24 | for (int j = 0; j < 2 * n; ++j) 25 | swap(temp[i][j], temp[r][j]); 26 | assert(fabs(temp[i][i]) > 1e-10); 27 | for (int k = i + 1; k < n; ++k) 28 | for (int j = 2 * n - 1; j >= i; --j) 29 | temp[k][j] -= temp[k][i] / temp[i][i] * temp[i][j]; 30 | } 31 | for (int i = n - 1; i >= 0; --i) { 32 | for (int j = i + 1; j < n; ++j) 33 | for (int k = n; k < 2 * n; ++k) 34 | temp[i][k] -= temp[j][k] * temp[i][j]; 35 | for (int k = n; k < 2 * n; ++k) 36 | temp[i][k] /= temp[i][i]; 37 | } 38 | for (int i = 0; i < n; ++i) 39 | for (int j = 0; j < n; ++j) 40 | res[i][j] = temp[i][n + j]; 41 | } 42 | } 43 | namespace inverse_mod { 44 | const long long mod = 998244353; 45 | using matrix = long long[maxn][maxn]; 46 | matrix temp; 47 | long long pow_mod(long long a, long long n) { 48 | long long ans = 1; 49 | while (n) { 50 | if (n & 1) 51 | ans = ans * a % mod; 52 | n >>= 1; 53 | a = a * a % mod; 54 | } 55 | return ans; 56 | } 57 | long long inv(long long x) { 58 | return pow_mod(x, mod - 2); 59 | } 60 | void inverse(const matrix& A, matrix& res, int n) { 61 | for (int i = 0; i < n; ++i) { 62 | for (int j = 0; j < n; ++j) 63 | temp[i][j] = A[i][j], temp[i][n + j] = 0; 64 | temp[i][n + i] = 1; 65 | } 66 | for (int i = 0; i < n; ++i) { 67 | int r = i; 68 | for (int j = i; j < n; ++j) { 69 | if (temp[j][i] != 0) { 70 | r = j; 71 | break; 72 | } 73 | } 74 | if (r != i) for (int j = 0; j < n * 2; ++j) 75 | swap(temp[i][j], temp[r][j]); 76 | auto pivot = inv(temp[i][i]); 77 | for (int k = i + 1; k < n; ++k) 78 | for (int j = 2 * n - 1; j >= i; --j) 79 | temp[k][j] = (temp[k][j] - temp[k][i] * pivot % mod * temp[i][j] % mod + mod) % mod; 80 | } 81 | for (int i = n - 1; i >= 0; --i) { 82 | for (int j = i + 1; j < n; ++j) { 83 | for (int k = n; k < n * 2; ++k) { 84 | temp[i][k] = (temp[i][k] - temp[j][k] * temp[i][j] % mod + mod) % mod; 85 | } 86 | } 87 | auto pivot = inv(temp[i][i]); 88 | for (int k = n; k < n * 2; ++k) 89 | temp[i][k] = temp[i][k] * pivot % mod; 90 | } 91 | for (int i = 0; i < n; ++i) 92 | for (int j = 0; j < n; ++j) 93 | res[i][j] = temp[i][n + j]; 94 | } 95 | } 96 | using namespace inverse_double; 97 | int main() { 98 | matrix res, A = { {0, 1, 2}, {1, 0, 3}, {4, -3, 8} }; 99 | inverse(A, res, 3); 100 | for (int i = 0; i < 3; ++i) { 101 | for (int j = 0; j < 3; ++j) 102 | printf("%.4f ", res[i][j]); 103 | printf("\n"); 104 | } 105 | return 0; 106 | } -------------------------------------------------------------------------------- /算法/数学算法/线性筛素数.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数学算法/线性筛素数.cpp -------------------------------------------------------------------------------- /算法/数学算法/线性规划.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数学算法/线性规划.cpp -------------------------------------------------------------------------------- /算法/数学算法/自适应辛普森积分.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | double f(double x) { //任意一个自定义的函数 7 | return x * x; 8 | } 9 | double simpson(double l, double r) { //计算函数f在区间[l, r]上的辛普森积分 10 | return (f(l) + f(r) + 4 * f((l + r) / 2)) * (r - l) / 6; 11 | } 12 | double solve(double l, double r, double eps, double val) { //递归求解积分 13 | double mid = (l + r) / 2; 14 | double L = simpson(l, mid), R = simpson(mid, r); 15 | if (fabs(L + R - val) <= 15 * eps) 16 | return L + R + (L + R - val) / 15; 17 | return solve(l, mid, eps / 2, L) + solve(mid, r, eps / 2, R); 18 | } 19 | double asme(double l, double r, double eps = 1e-7) { //自适应辛普森积分 20 | return solve(l, r, eps, simpson(l, r)); //求函数f在区间[l, r]上的自适应辛普森积分,eps指定精度 21 | } 22 | int main() { 23 | printf("%.15f\n", asme(0, 1)); //ans = 0.33333333333333 24 | return 0; 25 | } -------------------------------------------------------------------------------- /算法/数学算法/莫比乌斯反演.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数学算法/莫比乌斯反演.cpp -------------------------------------------------------------------------------- /算法/数学算法/行列式计算.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 矩阵树定理与部分扩展: 3 | 1. 给出一个无向无权图,设A为邻接矩阵, D为度数矩阵(D[i][i] = 结点i的度数,其他的无值)。 4 | 则基尔霍夫(Kirchhoff)矩阵即为: K = D - A, 5 | K的任意一个n - 1阶子式即为该图的生成树个数。 6 | 2. 把度数矩阵重新定义为D[i][i] = 与结点i相连的所有边的权值和, 7 | 把邻接矩阵重新定义为A[i][j] = 结点i与结点j之间所有边的权值和, 8 | 那么矩阵树定理求的就是: 所有生成树边权乘积的总和。 9 | 3. 有向图的情况也是可以做的,若D[i][i] = 结点i的入边的权值和,此时求的就是外向树 (从根向外), 10 | 若D[i][i] = 结点i的出边的权值和,此时求的就是内向树 (从外向根), 11 | 既然是有向的,那么就需要指定根,求行列式的时候去掉哪一行就是那一个元素为根。 12 | */ 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | using namespace std; 19 | const long long mod = 1e9 + 7; 20 | const int maxn = 500; 21 | using matrix = long long[maxn][maxn]; 22 | //时间复杂度n^3logm,如果超时可换成一般的高斯消元 23 | //注意矩阵A的下标从1开始 24 | long long det(matrix A, int n) { 25 | long long res = 1; 26 | for (int i = 0; i < n; ++i) { 27 | for (int j = i + 1; j < n; ++j) { 28 | while (A[j][i]) { //辗转相除法 29 | long long t = A[i][i] / A[j][i]; 30 | for (int k = i; k < n; ++k) { 31 | A[i][k] = (A[i][k] - t * A[j][k] % mod + mod) % mod; 32 | swap(A[i][k], A[j][k]); 33 | } 34 | res = -res; 35 | } 36 | } 37 | if (!A[i][i]) return 0; 38 | res = (res * A[i][i] % mod + mod) % mod; 39 | } 40 | return res; 41 | } 42 | int main() { //洛谷P6178 43 | static matrix A; 44 | int n, m, t; 45 | scanf("%d %d %d", &n, &m, &t); 46 | for (int i = 0; i < m; ++i) { 47 | int x, y, w; 48 | scanf("%d %d %d", &x, &y, &w); 49 | --x; --y; 50 | if (t == 0) { 51 | A[x][y] = (A[x][y] - w + mod) % mod; 52 | A[y][x] = (A[y][x] - w + mod) % mod; 53 | A[x][x] = (A[x][x] + w) % mod; 54 | A[y][y] = (A[y][y] + w) % mod; 55 | } 56 | else { 57 | if (x == 0) x = n - 1; else if (x == n - 1) x = 0; 58 | if (y == 0) y = n - 1; else if (y == n - 1) y = 0; 59 | A[x][y] = (A[x][y] - w + mod) % mod; 60 | A[y][y] = (A[y][y] + w) % mod; 61 | } 62 | } 63 | printf("%lld\n", det(A, n - 1)); 64 | return 0; 65 | } -------------------------------------------------------------------------------- /算法/数学算法/逆元.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数学算法/逆元.cpp -------------------------------------------------------------------------------- /算法/数学算法/递推式求解.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | const int mod = 1e9 + 7; 8 | int a[2100], c[2100], tmp[2100], tmpa[2100], res[2100], k; 9 | long long n; 10 | void mul(int* p, int* q, int* c) 11 | { 12 | memset(tmp, 0, sizeof(tmp)); 13 | for (int i = 0; i < k; i++) 14 | for (int j = 0; j < k; j++) 15 | tmp[i + j] = (1ll * p[i] * q[j] + tmp[i + j]) % mod; 16 | for (int i = 2 * k - 2; i >= k; i--) 17 | for (int j = 0; j < k; j++) 18 | tmp[i - j - 1] = (1ll * tmp[i] * c[j] + tmp[i - j - 1]) % mod; 19 | for (int i = 0; i < k; i++) 20 | p[i] = tmp[i]; 21 | } 22 | int solve(int* a, int* c, long long n) //a为初始值数组,c为系数矩阵 23 | { 24 | if (n < k) return a[n]; 25 | memset(tmpa, 0, sizeof(tmpa)); 26 | memset(res, 0, sizeof(res)); 27 | tmpa[1] = res[0] = 1; 28 | while (n) 29 | { 30 | if (n & 1) 31 | mul(res, tmpa, c); 32 | mul(tmpa, tmpa, c); 33 | n >>= 1; 34 | } 35 | int ans = 0; 36 | for (int i = 0; i < k; i++) 37 | ans = (ans + 1ll * a[i] * res[i]) % mod; 38 | return ans; 39 | } 40 | 41 | 42 | 43 | 44 | int pow_mod(int a, long long N) { 45 | int ans = 1; 46 | a %= mod; 47 | while (N) { 48 | if (N & 1) 49 | ans = 1ll * ans * a % mod; 50 | a = 1ll * a * a % mod; 51 | N >>= 1; 52 | } 53 | return ans; 54 | } 55 | int main() { 56 | int T; 57 | scanf("%d", &T); 58 | while (T--) { 59 | scanf("%d %lld", &k, &n); 60 | memset(a, 0, sizeof(a)); 61 | if (k == 1) { 62 | printf("1\n"); 63 | continue; 64 | } 65 | if (n == -1) { 66 | printf("%d\n", 2 * pow_mod(k + 1, mod - 2) % mod); 67 | continue; 68 | } 69 | int rev = pow_mod(k, mod - 2); 70 | for (int i = 0; i < k; ++i) 71 | c[i] = rev; 72 | a[0] = 1; 73 | for (int i = 1; i < k; ++i) { 74 | for (int j = i - k; j < i; ++j) if (j >= 0) { 75 | a[i] += 1ll * rev * a[j] % mod; 76 | if (a[i] >= mod)a[i] -= mod; 77 | } 78 | } 79 | int ans = solve(a, c, n); 80 | printf("%d\n", ans); 81 | } 82 | return 0; 83 | } -------------------------------------------------------------------------------- /算法/数学算法/雅可比方法.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 用雅可比方法求出实对称矩阵的特征值和特征向量,注意必须是实对称矩阵。 3 | 时间复杂度O(n^3),大常数。 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | using namespace std; 14 | const int maxn = 210; 15 | const double eps = 1e-8; 16 | using matrix = double[maxn][maxn]; 17 | using vec = array; 18 | using pair_t = pair; 19 | struct { 20 | matrix A, V; 21 | int column[maxn], n; 22 | void update(int r, int c, double v) { 23 | A[r][c] = v; 24 | if (column[r] == c || fabs(A[r][c]) > fabs(A[r][column[r]])) { 25 | for (int i = 0; i < n; ++i) if (i != r) 26 | if (fabs(A[r][i]) > fabs(A[r][column[r]])) 27 | column[r] = i; 28 | } 29 | } 30 | void Jacobi() { 31 | for (int i = 0; i < n; ++i) { 32 | for (int j = 0; j < n; ++j) 33 | V[i][j] = 0; 34 | V[i][i] = 1; 35 | column[i] = (i == 0 ? 1 : 0); 36 | } 37 | for (int i = 0; i < n; ++i) 38 | for (int j = 0; j < n; ++j) 39 | if (j != i && fabs(A[i][j]) > fabs(A[i][column[i]])) 40 | column[i] = j; 41 | for (int T = 0; ; ++T) { //迭代次数限制 42 | int x, y; 43 | double val = 0; 44 | for (int i = 0; i < n; ++i) 45 | if (fabs(A[i][column[i]]) > val) 46 | val = fabs(A[i][column[i]]), x = i, y = column[i]; 47 | if (val < eps) //精度限制 48 | break; 49 | double phi = atan2(-2 * A[x][y], A[y][y] - A[x][x]) / 2; 50 | double sinp = sin(phi), cosp = cos(phi); 51 | for (int i = 0; i < n; ++i) if (i != x && i != y) { 52 | double a = A[x][i] * cosp + A[y][i] * sinp; 53 | double b = A[x][i] * -sinp + A[y][i] * cosp; 54 | update(x, i, a); 55 | update(y, i, b); 56 | } 57 | for (int i = 0; i < n; ++i) if (i != x && i != y) { 58 | double a = A[i][x] * cosp + A[i][y] * sinp; 59 | double b = A[i][x] * -sinp + A[i][y] * cosp; 60 | update(i, x, a); 61 | update(i, y, b); 62 | } 63 | for (int i = 0; i < n; ++i) { 64 | double a = V[i][x] * cosp + V[i][y] * sinp; 65 | double b = V[i][x] * -sinp + V[i][y] * cosp; 66 | V[i][x] = a, V[i][y] = b; 67 | } 68 | double a = A[x][x] * cosp * cosp + A[y][y] * sinp * sinp + 2 * A[x][y] * cosp * sinp; 69 | double b = A[x][x] * sinp * sinp + A[y][y] * cosp * cosp - 2 * A[x][y] * cosp * sinp; 70 | double tmp = (A[y][y] - A[x][x]) * sin(2 * phi) / 2 + A[x][y] * cos(2 * phi); 71 | update(x, y, tmp); 72 | update(y, x, tmp); 73 | A[x][x] = a, A[y][y] = b; 74 | } 75 | } 76 | //返回特征值和特征向量组成的pair,按照特征值从大到小排序 77 | auto solve(const matrix& input, int n) { 78 | this->n = n; 79 | for (int i = 0; i < n; ++i) 80 | for (int j = 0; j < n; ++j) 81 | A[i][j] = input[i][j]; 82 | Jacobi(); 83 | vector result; 84 | for (int i = 0; i < n; ++i) 85 | result.emplace_back(A[i][i], vec()); 86 | for (int i = 0; i < n; ++i) 87 | for (int j = 0; j < n; ++j) 88 | result[i].second[j] = V[j][i]; 89 | sort(result.begin(), result.end(), greater()); 90 | return result; 91 | } 92 | }jacobi; 93 | matrix tmp = { {15.980000, 3.400000, -10.370000, }, 94 | {3.400000, 8.492000, -8.062000, }, 95 | {-10.370000, -8.062000, 11.572000, }, }; 96 | int main() { 97 | auto result = jacobi.solve(tmp, 3); 98 | for (int i = 0; i < 3; ++i) { 99 | auto eigenvalue = result[i].first; 100 | auto eigenvector = result[i].second; 101 | printf("(%f)", eigenvalue); 102 | for (int i = 0; i < 3; ++i) 103 | printf(" %f", eigenvector[i]); 104 | printf("\n"); 105 | } 106 | return 0; 107 | } -------------------------------------------------------------------------------- /算法/数据结构/AVL Tree.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数据结构/AVL Tree.cpp -------------------------------------------------------------------------------- /算法/数据结构/Euler-Tour-Tree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | const int maxn = 310000; //maxn至少为结点数 + 操作数的两倍 9 | namespace ett { 10 | //--------------------------Splay Tree------------------------------ 11 | //若要修改一个点的点权,应当先将其splay到根,然后修改,最后还要调用pushup维护。 12 | //调用完splay之后根结点会改变,应该用splay的返回值更新根结点。 13 | int ch[maxn][2], fa[maxn], stk[maxn], sum[maxn], key[maxn], cur; 14 | int alloc(int val) { 15 | int x = ++cur; 16 | ch[x][0] = ch[x][1] = fa[x] = 0; 17 | sum[x] = key[x] = val; 18 | return x; 19 | } 20 | inline bool son(int x) { 21 | return ch[fa[x]][1] == x; 22 | } 23 | inline void pushup(int x) { 24 | sum[x] = sum[ch[x][0]] + sum[ch[x][1]] + key[x]; 25 | } 26 | inline void pushdown(int x) {} 27 | void rotate(int x) { 28 | int y = fa[x], z = fa[y], c = son(x); 29 | if (fa[y]) 30 | ch[z][son(y)] = x; 31 | fa[x] = z; 32 | ch[y][c] = ch[x][!c]; 33 | fa[ch[y][c]] = y; 34 | ch[x][!c] = y; 35 | fa[y] = x; 36 | pushup(y); 37 | } 38 | void ascend(int x) { 39 | for (int y = fa[x]; y; rotate(x), y = fa[x]) if (fa[y]) 40 | son(x) ^ son(y) ? rotate(x) : rotate(y); 41 | pushup(x); 42 | } 43 | int splay(int x) { //没有pushdown操作时,可以直接用ascend替换splay 44 | int top = 0; 45 | for (int i = x; i; i = fa[i]) 46 | stk[++top] = i; 47 | while (top) 48 | pushdown(stk[top--]); 49 | ascend(x); 50 | return x; 51 | } 52 | template int merge(int x, int y, T... args) { 53 | if constexpr (sizeof...(args) == 0) { 54 | if (x == 0) return y; 55 | while (pushdown(x), ch[x][1]) 56 | x = ch[x][1]; 57 | ascend(x); 58 | ch[x][1] = y; fa[y] = x; 59 | pushup(x); 60 | return x; 61 | } 62 | else { 63 | return merge(merge(x, y), args...); 64 | } 65 | } 66 | inline int split(int x, int d) { 67 | int y = ch[x][d]; 68 | fa[y] = ch[x][d] = 0; 69 | pushup(x); 70 | return y; 71 | } 72 | //--------------------------Euler Tour Tree---------------------------- 73 | int idx; 74 | unordered_map locate; 75 | #define edge(x, y) (((1LL * (x)) << 30) + (y)) 76 | void init() { 77 | cur = idx = 0; 78 | locate.clear(); 79 | } 80 | inline int newnode(int val) { 81 | int x = ++idx; 82 | locate[edge(x, x)] = alloc(val); 83 | return x; 84 | } 85 | inline int newedge(int x, int y) { 86 | return locate[edge(x, y)] = alloc(0); 87 | } 88 | inline int changeroot(int x) { 89 | int a = locate[edge(x, x)]; 90 | splay(a); 91 | int b = split(a, 0); 92 | return merge(a, b); 93 | } 94 | inline int gettree(int x) { 95 | return splay(locate[edge(x, x)]); 96 | } 97 | inline bool sametree(int x, int y) { 98 | if (x == y) return true; 99 | auto a = locate[edge(x, x)]; 100 | auto b = locate[edge(y, y)]; 101 | splay(a); 102 | splay(b); 103 | return fa[a] != 0; 104 | } 105 | void link(int x, int y) { 106 | auto a = newedge(x, y); 107 | auto b = newedge(y, x); 108 | merge(changeroot(x), a, changeroot(y), b); 109 | } 110 | void cut(int x, int y) { 111 | auto a = locate[edge(x, y)]; 112 | auto b = locate[edge(y, x)]; 113 | splay(a); 114 | auto i = split(a, 0), j = split(a, 1); 115 | splay(b); 116 | bool flag = (i != 0 && fa[i] != 0) || b == i; 117 | auto L = split(b, 0), R = split(b, 1); 118 | if (flag) 119 | merge(L, j); 120 | else 121 | merge(i, R); 122 | } 123 | } 124 | using namespace ett; 125 | int node[maxn]; 126 | //访问结点node[x]所在树的权值和:sum[gettree(node[x])] 127 | //修改结点node[x]的权值为v:int a = gettree(node[x]); key[a] = v; pushup(a); 128 | int main() { //洛谷 P4219 129 | //freopen("in.txt", "r", stdin); 130 | int n, q; 131 | scanf("%d %d", &n, &q); 132 | init(); 133 | for (int i = 1; i <= n; ++i) 134 | node[i] = newnode(1); 135 | for (int i = 0; i < q; ++i) { 136 | char tp; 137 | int x, y; 138 | scanf(" %c %d %d", &tp, &x, &y); 139 | if (tp == 'A') { 140 | link(node[x], node[y]); 141 | } 142 | else { 143 | cut(node[x], node[y]); 144 | long long a = sum[gettree(node[x])]; 145 | long long b = sum[gettree(node[y])]; 146 | printf("%lld\n", a * b); 147 | link(node[x], node[y]); 148 | } 149 | } 150 | return 0; 151 | } -------------------------------------------------------------------------------- /算法/数据结构/SplayTree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | const int maxn = 110000; 9 | //若要修改一个点的点权,应当先将其splay到根,然后修改,最后还要调用pushup维护。 10 | //调用完splay之后根结点会改变,应该用splay的返回值更新根结点。 11 | namespace splay_tree { 12 | int ch[maxn][2], fa[maxn], stk[maxn], rev[maxn], sz[maxn], key[maxn], cur; 13 | void init() { 14 | cur = 0; 15 | } 16 | int newnode(int val) { 17 | int x = ++cur; 18 | ch[x][0] = ch[x][1] = fa[x] = rev[x] = 0; 19 | sz[x] = 1; 20 | key[x] = val; 21 | return x; 22 | } 23 | inline bool son(int x) { 24 | return ch[fa[x]][1] == x; 25 | } 26 | inline void pushup(int x) { 27 | sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1; 28 | } 29 | inline void pushdown(int x) { 30 | if (rev[x]) { 31 | rev[x] = 0; 32 | swap(ch[x][0], ch[x][1]); 33 | rev[ch[x][0]] ^= 1; 34 | rev[ch[x][1]] ^= 1; 35 | } 36 | } 37 | void rotate(int x) { 38 | int y = fa[x], z = fa[y], c = son(x); 39 | if (fa[y]) 40 | ch[z][son(y)] = x; 41 | fa[x] = z; 42 | ch[y][c] = ch[x][!c]; 43 | fa[ch[y][c]] = y; 44 | ch[x][!c] = y; 45 | fa[y] = x; 46 | pushup(y); 47 | } 48 | void ascend(int x) { 49 | for (int y = fa[x]; y; rotate(x), y = fa[x]) if (fa[y]) 50 | son(x) ^ son(y) ? rotate(x) : rotate(y); 51 | pushup(x); 52 | } 53 | int splay(int x) { //没有pushdown操作时,可以直接用ascend替换splay 54 | int top = 0; 55 | for (int i = x; i; i = fa[i]) 56 | stk[++top] = i; 57 | while (top) 58 | pushdown(stk[top--]); 59 | ascend(x); 60 | return x; 61 | } 62 | int splay(int x, int k) { //将以x为根的子树中的第k个结点旋转到根结点 63 | while (pushdown(x), k != sz[ch[x][0]] + 1) { 64 | if (k <= sz[ch[x][0]]) 65 | x = ch[x][0]; 66 | else 67 | k -= sz[ch[x][0]] + 1, x = ch[x][1]; 68 | } 69 | if (x) ascend(x); 70 | return x; 71 | } 72 | template int merge(int x, int y, T... args) { 73 | if constexpr (sizeof...(args) == 0) { 74 | if (x == 0) return y; //swap(x, y); 75 | x = splay(x, sz[x]); 76 | ch[x][1] = y; fa[y] = x; 77 | pushup(x); 78 | return x; 79 | } 80 | else { 81 | return merge(merge(x, y), args...); 82 | } 83 | } 84 | auto split(int x, int pos) { //分成两个区间[1, pos - 1]和[pos, n] 85 | if (pos == sz[x] + 1) 86 | return make_pair(x, 0); 87 | x = splay(x, pos); 88 | int y = ch[x][0]; 89 | fa[y] = ch[x][0] = 0; 90 | pushup(x); 91 | return make_pair(y, x); 92 | } 93 | auto extract(int x, int L, int R) { 94 | auto [left, y] = split(x, L); 95 | auto [mid, right] = split(y, R - L + 2); 96 | return make_tuple(left, mid, right); 97 | } 98 | void traverse(int x) { 99 | if (x != 0) { 100 | traverse(ch[x][0]); 101 | printf("%d ", key[x]); 102 | //printf("%d (left: %d, right: %d) sz(%d) key(%d)\n", x, ch[x][0], ch[x][1], sz[x], key[x]); 103 | traverse(ch[x][1]); 104 | } 105 | } 106 | } 107 | 108 | using namespace splay_tree; 109 | int main() { 110 | init(); 111 | int nd[50], root = 0; 112 | for (int i = 1; i <= 10; ++i) { 113 | nd[i] = newnode(i); 114 | root = merge(root, nd[i]); 115 | } 116 | traverse(get<1>(extract(root, 3, 10))); printf("\n"); 117 | return 0; 118 | } -------------------------------------------------------------------------------- /算法/数据结构/Treap.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数据结构/Treap.cpp -------------------------------------------------------------------------------- /算法/数据结构/link-cut-tree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | const int maxn = 110000; 8 | //若要修改一个点的点权,应当先将其splay到根,然后修改,最后还要调用pushup维护。 9 | namespace lct { 10 | int ch[maxn][2], fa[maxn], stk[maxn], rev[maxn]; 11 | void init() { //初始化link-cut-tree 12 | memset(ch, 0, sizeof(ch)); 13 | memset(fa, 0, sizeof(fa)); 14 | memset(rev, 0, sizeof(rev)); 15 | } 16 | inline bool son(int x) { 17 | return ch[fa[x]][1] == x; 18 | } 19 | inline bool isroot(int x) { 20 | return ch[fa[x]][1] != x && ch[fa[x]][0] != x; 21 | } 22 | inline void reverse(int x) { //给结点x打上反转标记 23 | swap(ch[x][1], ch[x][0]); 24 | rev[x] ^= 1; 25 | } 26 | inline void pushup(int x) { } 27 | inline void pushdown(int x) { 28 | if (rev[x]) { 29 | reverse(ch[x][0]); 30 | reverse(ch[x][1]); 31 | rev[x] = 0; 32 | } 33 | } 34 | void rotate(int x) { 35 | int y = fa[x], z = fa[y], c = son(x); 36 | if (!isroot(y)) 37 | ch[z][son(y)] = x; 38 | fa[x] = z; 39 | ch[y][c] = ch[x][!c]; 40 | fa[ch[y][c]] = y; 41 | ch[x][!c] = y; 42 | fa[y] = x; 43 | pushup(y); 44 | } 45 | void splay(int x) { 46 | int top = 0; 47 | stk[++top] = x; 48 | for (int i = x; !isroot(i); i = fa[i]) 49 | stk[++top] = fa[i]; 50 | while (top) 51 | pushdown(stk[top--]); 52 | for (int y = fa[x]; !isroot(x); rotate(x), y = fa[x]) if (!isroot(y)) 53 | son(x) ^ son(y) ? rotate(x) : rotate(y); 54 | pushup(x); 55 | } 56 | void access(int x) { 57 | for (int y = 0; x; y = x, x = fa[x]) { 58 | splay(x); 59 | ch[x][1] = y; 60 | pushup(x); 61 | } 62 | } 63 | void makeroot(int x) { //将x变为树的新的根结点 64 | access(x); 65 | splay(x); 66 | reverse(x); 67 | } 68 | int findroot(int x) { //返回x所在树的根结点 69 | access(x); 70 | splay(x); 71 | while (ch[x][0]) 72 | pushdown(x), x = ch[x][0]; 73 | return x; 74 | } 75 | void split(int x, int y) { //提取出来y到x之间的路径,并将y作为根结点 76 | makeroot(x); 77 | access(y); 78 | splay(y); 79 | } 80 | void cut(int x) { //断开结点x与它的父结点之间的边 81 | access(x); 82 | splay(x); 83 | ch[x][0] = fa[ch[x][0]] = 0; 84 | pushup(x); 85 | } 86 | void cut(int x, int y) { //切断x与y相连的边(必须保证x与y在一棵树中) 87 | makeroot(x); //将x置为整棵树的根 88 | cut(y); //删除y与其父结点之间的边 89 | } 90 | void link(int x, int y) { //连接x与y(必须保证x和y属于不同的树) 91 | makeroot(x); 92 | fa[x] = y; 93 | } 94 | bool sametree(int x, int y) { //判断结点x与y是否属于同一棵树 95 | makeroot(x); 96 | return findroot(y) == x; 97 | } 98 | } 99 | int main() { 100 | return 0; 101 | } -------------------------------------------------------------------------------- /算法/数据结构/link-cut-tree(指针).cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | const int maxn = 400000; 8 | const int inf = 1 << 30; 9 | 10 | struct node { 11 | node* p, * ch[2]; 12 | int mx, rev, val, add; 13 | }nodes[maxn], * cur, * nil; 14 | node* newnode(int key) { 15 | cur->p = cur->ch[0] = cur->ch[1] = nil; 16 | cur->mx = cur->val = key; 17 | cur->add = cur->rev = 0; 18 | return cur++; 19 | } 20 | void init() { 21 | cur = nodes; 22 | nil = newnode(-inf); 23 | } 24 | bool isroot(node* x) { 25 | return x->p == nil || x->p->ch[0] != x && x->p->ch[1] != x; 26 | } 27 | void increase(node* x, int v) { 28 | x->val += v; 29 | x->add += v; 30 | x->mx += v; 31 | } 32 | void pushup(node* x) { 33 | x->mx = x->val; 34 | if (x->ch[0] != nil) 35 | x->mx = max(x->mx, x->ch[0]->mx); 36 | if (x->ch[1] != nil) 37 | x->mx = max(x->mx, x->ch[1]->mx); 38 | } 39 | void pushdown(node* x) { 40 | if (x->rev) { 41 | x->rev = 0; 42 | if (x->ch[0] != nil) x->ch[0]->rev ^= 1; 43 | if (x->ch[1] != nil) x->ch[1]->rev ^= 1; 44 | swap(x->ch[0], x->ch[1]); 45 | } 46 | if (x->add) { 47 | if (x->ch[0] != nil) 48 | increase(x->ch[0], x->add); 49 | if (x->ch[1] != nil) 50 | increase(x->ch[1], x->add); 51 | x->add = 0; 52 | } 53 | } 54 | void rotate(node* x, int d) { 55 | if (isroot(x)) return; 56 | node* y = x->p; 57 | y->ch[!d] = x->ch[d]; 58 | x->ch[d]->p = y; 59 | x->p = y->p; 60 | if (!isroot(y)) 61 | y->p->ch[y == y->p->ch[1]] = x; 62 | x->ch[d] = y; 63 | y->p = x; 64 | pushup(y); 65 | } 66 | void splay(node* x) { //若要修改一个点的权值则要先将其伸展到根 67 | static node* sta[maxn]; 68 | int top = 1; 69 | sta[0] = x; 70 | for (node* y = x; !isroot(y); y = y->p) 71 | sta[top++] = y->p; 72 | while (top) pushdown(sta[--top]); 73 | while (!isroot(x)) { 74 | node* y = x->p; 75 | if (isroot(y)) 76 | rotate(x, x == y->ch[0]); 77 | else { 78 | int d = y == y->p->ch[0]; 79 | if (x == y->ch[d]) 80 | rotate(x, !d); 81 | else 82 | rotate(y, d); 83 | rotate(x, d); 84 | } 85 | } 86 | pushup(x); 87 | } 88 | node* access(node* x) { //打通结点x到根结点的路径 89 | node* y = nil; 90 | while (x != nil) { 91 | splay(x); 92 | y->p = x; 93 | x->ch[1] = y; 94 | pushup(x); 95 | y = x; 96 | x = x->p; 97 | } 98 | return y; 99 | } 100 | void changeroot(node* x) { //将x置为整棵树的根结点 101 | access(x)->rev ^= 1; 102 | } 103 | void link(node* x, node* y) { //将结点x与y连接起来(必须保证x与y属于不同的树) 104 | access(x); 105 | splay(x); 106 | x->rev ^= 1; 107 | x->p = y; 108 | } 109 | void cut(node* x) { //断开结点x与它的父结点之间的边 110 | access(x); 111 | splay(x); 112 | x->ch[0] = x->ch[0]->p = nil; 113 | pushup(x); 114 | } 115 | void cut(node* x, node* y) { //断开结点x与y之间的边 116 | changeroot(x); //将x置为整棵树的根 117 | cut(y); //删除y与其父结点之间的边 118 | } 119 | node* getroot(node* x) { //返回结点x所在的树当前的根结点 120 | access(x); 121 | splay(x); 122 | while (pushdown(x), x->ch[0] != nil) 123 | x = x->ch[0]; 124 | splay(x); 125 | return x; 126 | } 127 | bool sametree(node* x, node* y) { //判断结点x与y是否属于同一棵树 128 | changeroot(x); 129 | return getroot(y) == x; 130 | } 131 | 132 | int n, m, x, y, w; 133 | int eu[maxn], ev[maxn]; 134 | int main() 135 | { 136 | //freopen("in.txt", "r", stdin); 137 | while (scanf("%d", &n) != -1) 138 | { 139 | init(); 140 | for (int i = 1; i < n; i++) 141 | scanf("%d%d", &eu[i], &ev[i]); 142 | for (int i = 1; i <= n; i++) 143 | { 144 | int a; 145 | scanf("%d", &a); 146 | newnode(a); 147 | } 148 | for (int i = 1; i < n; i++) 149 | link(nodes + eu[i], nodes + ev[i]); 150 | scanf("%d", &m); 151 | for (int i = 1; i <= m; i++) 152 | { 153 | scanf("%d", &x); 154 | if (x == 1) 155 | { 156 | scanf("%d%d", &x, &y); 157 | if (sametree(nodes + x, nodes + y)) 158 | { 159 | printf("-1\n"); 160 | continue; 161 | } 162 | link(nodes + x, nodes + y); 163 | } 164 | else if (x == 2) 165 | { 166 | scanf("%d%d", &x, &y); 167 | if (x == y || !sametree(nodes + x, nodes + y)) 168 | { 169 | printf("-1\n"); 170 | continue; 171 | } 172 | changeroot(nodes + x); 173 | cut(nodes + y); 174 | } 175 | else if (x == 3) 176 | { 177 | scanf("%d%d%d", &w, &x, &y); 178 | if (!sametree(nodes + x, nodes + y)) 179 | { 180 | printf("-1\n"); 181 | continue; 182 | } 183 | changeroot(nodes + x); 184 | access(nodes + y); 185 | splay(nodes + y); 186 | node* q = nodes + y; 187 | increase(q, w); 188 | } 189 | else 190 | { 191 | scanf("%d%d", &x, &y); 192 | if (!sametree(nodes + x, nodes + y)) 193 | { 194 | printf("-1\n"); 195 | continue; 196 | } 197 | changeroot(nodes + x); 198 | access(nodes + y); 199 | splay(nodes + y); 200 | printf("%d\n", (nodes + y)->mx); 201 | } 202 | } 203 | printf("\n"); 204 | } 205 | return 0; 206 | } 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /算法/数据结构/link-cut-tree(维护子树).cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 用link-cut-tree维护子树信息:对于维护的信息,每个点要开两个属性,其中一个是在原树中的子树信息, 3 | 另一个是在LCT中虚子树的信息。除了要在pushup中维护这些信息外,还要在access和link函数中维护。 4 | 若要访问一个结点x的子树信息,应当先将其access到根,此时x->size表示x的子树中除去x外所有结点的信息(大小), 5 | 与x自身的信息结合即可得到x的子树信息(x->size + 1即为子树大小)。 6 | 若要访问整棵树的信息,可以考虑结合changeroot函数,并使用结点的sum信息。 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | using namespace std; 14 | const int maxn = 400000; 15 | 16 | 17 | struct node { 18 | node* p, * ch[2]; 19 | int rev, sum, size; //sum维护当前点的虚子树与实子树大小之和,size是在LCT中虚子树的大小 20 | }nodes[maxn], * cur, * nil; 21 | node* newnode() { 22 | cur->p = cur->ch[0] = cur->ch[1] = nil; 23 | cur->sum = 1; 24 | cur->size = 0; 25 | cur->rev = 0; 26 | return cur++; 27 | } 28 | void init() { 29 | cur = nodes; 30 | nil = newnode(); 31 | nil->sum = 0; 32 | } 33 | bool isroot(node* x) { 34 | return x->p == nil || x->p->ch[0] != x && x->p->ch[1] != x; 35 | } 36 | void pushup(node* x) { 37 | x->sum = x->ch[0]->sum + x->ch[1]->sum + x->size + 1; 38 | } 39 | void pushdown(node* x) { 40 | if (x->rev) { 41 | x->rev = 0; 42 | if (x->ch[0] != nil) x->ch[0]->rev ^= 1; 43 | if (x->ch[1] != nil) x->ch[1]->rev ^= 1; 44 | swap(x->ch[0], x->ch[1]); 45 | } 46 | } 47 | void rotate(node* x, int d) { 48 | if (isroot(x)) return; 49 | node* y = x->p; 50 | y->ch[!d] = x->ch[d]; 51 | x->ch[d]->p = y; 52 | x->p = y->p; 53 | if (!isroot(y)) 54 | y->p->ch[y == y->p->ch[1]] = x; 55 | x->ch[d] = y; 56 | y->p = x; 57 | pushup(y); 58 | } 59 | void splay(node* x) { //若要修改一个点的权值则要先将其伸展到根 60 | static node* sta[maxn]; 61 | int top = 1; 62 | sta[0] = x; 63 | for (node* y = x; !isroot(y); y = y->p) 64 | sta[top++] = y->p; 65 | while (top) pushdown(sta[--top]); 66 | while (!isroot(x)) { 67 | node* y = x->p; 68 | if (isroot(y)) 69 | rotate(x, x == y->ch[0]); 70 | else { 71 | int d = y == y->p->ch[0]; 72 | if (x == y->ch[d]) 73 | rotate(x, !d); 74 | else 75 | rotate(y, d); 76 | rotate(x, d); 77 | } 78 | } 79 | pushup(x); 80 | } 81 | node* access(node* x) { //打通结点x到根结点的路径 82 | node* y = nil; 83 | while (x != nil) { 84 | splay(x); 85 | x->size += x->ch[1]->sum - y->sum; //动态维护虚子树信息 86 | y->p = x; 87 | x->ch[1] = y; 88 | pushup(x); 89 | y = x; 90 | x = x->p; 91 | } 92 | return y; 93 | } 94 | void changeroot(node* x) { //将x置为整棵树的根结点 95 | access(x)->rev ^= 1; 96 | splay(x); //将x伸展到splay的根结点,这样才能保证link的正确性 97 | } 98 | void link(node* x, node* y) { //将结点x与y连接起来(必须保证x与y属于不同的树且不为nil结点) 99 | changeroot(x); 100 | changeroot(y); 101 | x->p = y; 102 | y->size += x->sum; //动态维护虚子树信息 103 | pushup(y); 104 | } 105 | void cut(node* x) { //断开结点x与它的父结点之间的边 106 | access(x); 107 | splay(x); 108 | x->ch[0] = x->ch[0]->p = nil; 109 | pushup(x); 110 | } 111 | void cut(node* x, node* y) { //断开结点x与y之间的边 112 | changeroot(x); //将x置为整棵树的根 113 | cut(y); //删除y与其父结点之间的边 114 | } 115 | node* getroot(node* x) { //返回结点x所在的树当前的根结点 116 | access(x); 117 | splay(x); 118 | while (pushdown(x), x->ch[0] != nil) 119 | x = x->ch[0]; 120 | splay(x); 121 | return x; 122 | } 123 | bool sametree(node* x, node* y) { //判断结点x与y是否属于同一棵树 124 | changeroot(x); 125 | return getroot(y) == x; 126 | } 127 | 128 | 129 | 130 | node* nd[maxn]; 131 | int main() { 132 | //freopen("in.txt", "r", stdin); 133 | int n, q; 134 | scanf("%d %d", &n, &q); 135 | init(); 136 | for (int i = 1; i <= n; ++i) 137 | nd[i] = newnode(); 138 | while (q--) { 139 | char tp; 140 | int x, y; 141 | scanf(" %c %d %d", &tp, &x, &y); 142 | if (tp == 'A') { 143 | link(nd[x], nd[y]); 144 | } 145 | else { 146 | changeroot(nd[x]); 147 | access(nd[y]); 148 | long long L = nd[y]->size + 1; //将虚子树的大小加1,即为子树大小。 149 | changeroot(nd[y]); 150 | access(nd[x]); 151 | long long R = nd[x]->size + 1; 152 | printf("%lld\n", L * R); 153 | } 154 | } 155 | return 0; 156 | } 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /算法/数据结构/link-cut-tree(边权).cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | const int maxn = 400000; 8 | const int inf = 1 << 30; 9 | 10 | struct node { //mn记录最小值,pos记录最小值所在的结点 11 | node* p, *ch[2], *pos; 12 | int mn, rev, val; 13 | }nodes[maxn], * cur, * nil; 14 | pair edge[maxn]; 15 | node* newnode(int key) { 16 | cur->p = cur->ch[0] = cur->ch[1] = nil; 17 | cur->mn = cur->val = key; 18 | cur->rev = 0; 19 | return cur++; 20 | } 21 | void init() { 22 | cur = nodes; 23 | nil = newnode(inf); 24 | } 25 | bool isroot(node* x) { 26 | return x->p == nil || x->p->ch[0] != x && x->p->ch[1] != x; 27 | } 28 | void pushup(node* x) { 29 | x->mn = x->val; 30 | x->pos = x; 31 | if (x->ch[0] != nil && x->ch[0]->mn < x->mn) { 32 | x->mn = x->ch[0]->mn; 33 | x->pos = x->ch[0]->pos; 34 | } 35 | if (x->ch[1] != nil && x->ch[1]->mn < x->mn) { 36 | x->mn = x->ch[1]->mn; 37 | x->pos = x->ch[1]->pos; 38 | } 39 | } 40 | void pushdown(node* x) { 41 | if (x->rev) { 42 | x->rev = 0; 43 | if (x->ch[0] != nil) x->ch[0]->rev ^= 1; 44 | if (x->ch[1] != nil) x->ch[1]->rev ^= 1; 45 | swap(x->ch[0], x->ch[1]); 46 | } 47 | } 48 | void rotate(node* x, int d) { 49 | if (isroot(x)) return; 50 | node* y = x->p; 51 | y->ch[!d] = x->ch[d]; 52 | x->ch[d]->p = y; 53 | x->p = y->p; 54 | if (!isroot(y)) 55 | y->p->ch[y == y->p->ch[1]] = x; 56 | x->ch[d] = y; 57 | y->p = x; 58 | pushup(y); 59 | } 60 | void splay(node* x) { 61 | static node* sta[maxn]; 62 | int top = 1; 63 | sta[0] = x; 64 | for (node* y = x; !isroot(y); y = y->p) 65 | sta[top++] = y->p; 66 | while (top) pushdown(sta[--top]); 67 | while (!isroot(x)) { 68 | node* y = x->p; 69 | if (isroot(y)) 70 | rotate(x, x == y->ch[0]); 71 | else { 72 | int d = y == y->p->ch[0]; 73 | if (x == y->ch[d]) 74 | rotate(x, !d); 75 | else 76 | rotate(y, d); 77 | rotate(x, d); 78 | } 79 | } 80 | pushup(x); 81 | } 82 | node* access(node* x) { //打通结点x到根结点的路径 83 | node* y = nil; 84 | while (x != nil) { 85 | splay(x); 86 | y->p = x; 87 | x->ch[1] = y; 88 | pushup(x); 89 | y = x; 90 | x = x->p; 91 | } 92 | return y; 93 | } 94 | void changeroot(node* x) { //将x置为整棵树的根结点 95 | access(x)->rev ^= 1; 96 | } 97 | void link(node* x, node* y) { 98 | access(x); 99 | splay(x); 100 | x->rev ^= 1; 101 | x->p = y; 102 | } 103 | node *link(node* x, node* y, int value) { //在结点x与结点y之间连接一条权值为v的边(必须保证x与y属于不同的树) 104 | node* e = newnode(value); 105 | link(x, e); 106 | link(y, e); 107 | edge[e - nodes] = make_pair(x, y); 108 | return e; //返回新建的边对应的结点 109 | } 110 | void cut(node* x, node* y) { //断开结点x与y之间的边 111 | changeroot(x); //将x置为整棵树的根 112 | access(y); 113 | splay(y); 114 | y->ch[0] = y->ch[0]->p = nil; 115 | pushup(y); 116 | } 117 | void cut(node* x) { //删除结点x所代表的边 118 | pair pr = edge[x - nodes]; 119 | cut(pr.first, x); 120 | cut(pr.second, x); 121 | edge[x - nodes] = pair(0, 0); 122 | } 123 | bool exist(node* x) { //检查点x所对应的边是否还存在于树中(没有被删除) 124 | return edge[x - nodes].first != 0; 125 | } 126 | node* getroot(node* x) { //返回结点x所在的树当前的根结点 127 | access(x); 128 | splay(x); 129 | while (pushdown(x), x->ch[0] != nil) 130 | x = x->ch[0]; 131 | splay(x); 132 | return x; 133 | } 134 | bool sametree(node* x, node* y) { //判断结点x与y是否属于同一棵树 135 | changeroot(x); 136 | return getroot(y) == x; 137 | } 138 | 139 | int main() { 140 | 141 | } 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /算法/数据结构/pb-ds平衡树.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数据结构/pb-ds平衡树.cpp -------------------------------------------------------------------------------- /算法/数据结构/rope.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数据结构/rope.cpp -------------------------------------------------------------------------------- /算法/数据结构/zkw线段树(单点加-区间加-单点查询-区间求和).cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数据结构/zkw线段树(单点加-区间加-单点查询-区间求和).cpp -------------------------------------------------------------------------------- /算法/数据结构/主席树.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数据结构/主席树.cpp -------------------------------------------------------------------------------- /算法/数据结构/动态主席树.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数据结构/动态主席树.cpp -------------------------------------------------------------------------------- /算法/数据结构/动态开点线段树.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | const int maxn = 210000, offset = 210000; 10 | const int inf = 1 << 30; 11 | struct tmp { 12 | int data[maxn * 5]; 13 | int& operator[] (int idx) { 14 | return data[idx + offset]; 15 | } 16 | }A; 17 | 18 | #define lc t[p].lchild 19 | #define rc t[p].rchild 20 | int cur = 0, tot, mn, mx; 21 | struct segment { 22 | int lchild, rchild; 23 | int sum, min, max, set, add; 24 | }t[maxn * 4]; 25 | void init() { 26 | cur = 0; 27 | } 28 | inline void newnode(int& p) { 29 | if (p == 0) { 30 | p = ++cur; 31 | memset(&t[p], 0, sizeof(t[p])); 32 | t[p].set = -1; 33 | } 34 | } 35 | inline void maintain(int p) { 36 | t[p].sum = t[lc].sum + t[rc].sum; 37 | t[p].min = min(t[lc].min, t[rc].min); 38 | t[p].max = max(t[lc].max, t[rc].max); 39 | } 40 | inline void mark(int &p, int setv, int addv, int x, int y) { //给结点打标记 41 | newnode(p); 42 | if (setv >= 0) { 43 | t[p].set = setv; t[p].add = 0; 44 | t[p].min = t[p].max = setv; 45 | t[p].sum = setv * (y - x + 1); 46 | } 47 | if (addv) { 48 | t[p].add += addv; 49 | t[p].min += addv; 50 | t[p].max += addv; 51 | t[p].sum += addv * (y - x + 1); 52 | } 53 | } 54 | inline void pushdown(int p, int x, int y) { //pushdown将标记传递给子结点,不影响当前结点的信息。 55 | int mid = (x + y) >> 1; 56 | mark(lc, t[p].set, t[p].add, x, mid); 57 | mark(rc, t[p].set, t[p].add, mid+1, y); 58 | t[p].set = -1; 59 | t[p].add = 0; 60 | } 61 | void update(int &p, int L, int R, int op, int v, int x, int y) { 62 | newnode(p); 63 | if (L <= x && R >= y) { 64 | if (op == 0) 65 | mark(p, -1, v, x, y); 66 | else 67 | mark(p, v, 0, x, y); 68 | } 69 | else { 70 | pushdown(p, x, y); //如果没有pushdown只需要在最后调用一次maintain即可。 71 | int mid = (x + y) >> 1; 72 | if (L <= mid) 73 | update(lc, L, R, op, v, x, mid); 74 | if (R > mid) 75 | update(rc, L, R, op, v, mid+1, y); 76 | maintain(p); 77 | } 78 | } 79 | void query(int &p, int L, int R, int x, int y) { //调用之前要设置:mn = inf; mx = -inf; tot = 0; 80 | newnode(p); 81 | if (L <= x && R >= y) { 82 | tot += t[p].sum; 83 | mn = min(mn, t[p].min); 84 | mx = max(mx, t[p].max); 85 | } 86 | else { 87 | pushdown(p, x, y); 88 | int mid = (x + y) >> 1; 89 | if (L <= mid) 90 | query(lc, L, R, x, mid); 91 | if (R > mid) 92 | query(rc, L, R, mid+1, y); 93 | } 94 | } 95 | int main() { 96 | default_random_engine e; 97 | int n = 100000, m = 100000; 98 | uniform_int_distribution d(-n, n); 99 | for (int i = -n; i <= n; ++i) 100 | A[i] = 0; 101 | init(); 102 | int root = 0; 103 | for (int i = 1; i <= m; ++i) { 104 | int op = rand() % 3, a = d(e), b = d(e), v = rand(); 105 | int L = min(a, b), R = max(a, b); 106 | if (op == 0) { 107 | for (int i = L; i <= R; ++i) 108 | A[i] += v; 109 | update(root, L, R, op, v, -n, n); 110 | } 111 | else if (op == 1) { 112 | for (int i = L; i <= R; ++i) 113 | A[i] = v; 114 | update(root, L, R, op, v, -n, n); 115 | } 116 | else { 117 | mn = inf; mx = -inf; tot = 0; 118 | query(root, L, R, -n, n); 119 | if (mn != *min_element(A.data + offset + L, A.data + offset + R + 1)) 120 | abort(); 121 | if (mx != *max_element(A.data + offset + L, A.data + offset + R + 1)) 122 | abort(); 123 | if (tot != accumulate(A.data + offset + L, A.data + offset + R + 1, 0)) 124 | abort(); 125 | } 126 | } 127 | return 0; 128 | } -------------------------------------------------------------------------------- /算法/数据结构/动态开点线段树(单点加-区间求和-合并).cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数据结构/动态开点线段树(单点加-区间求和-合并).cpp -------------------------------------------------------------------------------- /算法/数据结构/可修改优先队列.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | using namespace std; 10 | template struct ModifiablePriorityQueue { // 大根堆 11 | int data[maxsize * 2]; 12 | int pos[maxsize * 2]; 13 | T value[maxsize * 2]; 14 | int sz; 15 | ModifiablePriorityQueue() : pos(), sz(0) {} 16 | void up(int i) { // 值变大 17 | auto index = data[i]; 18 | auto val = value[index]; 19 | while (i > 1) { 20 | int fa = i / 2; 21 | if (val > value[data[fa]]) { 22 | data[i] = data[fa]; 23 | pos[data[i]] = i; 24 | i = fa; 25 | } 26 | else { 27 | break; 28 | } 29 | } 30 | data[i] = index; 31 | pos[index] = i; 32 | } 33 | void down(int i) { // 值变小 34 | auto index = data[i]; 35 | auto val = value[index]; 36 | while (i * 2 <= sz) { 37 | int child = i * 2; 38 | if (i * 2 + 1 <= sz && value[data[i * 2 + 1]] > value[data[child]]) { 39 | child = i * 2 + 1; 40 | } 41 | if (val < value[data[child]]) { 42 | data[i] = data[child]; 43 | pos[data[i]] = i; 44 | i = child; 45 | } 46 | else { 47 | break; 48 | } 49 | } 50 | data[i] = index; 51 | pos[index] = i; 52 | } 53 | void push(int index, const T& val) { 54 | sz += 1; 55 | data[sz] = index; 56 | value[index] = val; 57 | up(sz); 58 | } 59 | void pop() { 60 | pos[data[1]] = 0; 61 | data[1] = data[sz]; 62 | sz -= 1; 63 | if (sz > 0) { 64 | down(1); 65 | } 66 | } 67 | void erase(int index) { 68 | index = pos[index]; 69 | pos[data[index]] = 0; 70 | data[index] = data[sz]; 71 | sz -= 1; 72 | if (index <= sz) { 73 | down(index); 74 | } 75 | } 76 | void modify(int index, const T& val) { 77 | if (pos[index] == 0) { 78 | push(index, val); 79 | } 80 | else if (val > value[index]) { 81 | value[index] = val; 82 | up(pos[index]); 83 | } 84 | else { 85 | value[index] = val; 86 | down(pos[index]); 87 | } 88 | } 89 | int top() const { 90 | return data[1]; 91 | } 92 | T get(int index) const { 93 | return value[index]; 94 | } 95 | T maximum() const { 96 | return value[data[1]]; 97 | } 98 | bool contains(int index) const { 99 | return pos[index] != 0; 100 | } 101 | int size() const { 102 | return sz; 103 | } 104 | bool empty() const { 105 | return sz == 0; 106 | } 107 | }; 108 | int main() { // 洛谷 P4779 109 | static int d[210000]; 110 | static vector> G[210000]; 111 | int n, m, s; 112 | scanf("%d %d %d", &n, &m, &s); 113 | for (int i = 0; i < m; ++i) { 114 | int x, y, z; 115 | scanf("%d %d %d", &x, &y, &z); 116 | G[x].emplace_back(y, z); 117 | } 118 | static ModifiablePriorityQueue Q; 119 | memset(d, 0x3f, sizeof(d)); 120 | d[s] = 0; 121 | Q.push(s, 0); 122 | while (!Q.empty()) { 123 | auto x = Q.top(); Q.pop(); 124 | for (auto [y, w] : G[x]) { 125 | if (d[y] > d[x] + w) { 126 | d[y] = d[x] + w; 127 | Q.modify(y, -d[y]); 128 | } 129 | } 130 | } 131 | for (int i = 1; i <= n; ++i) { 132 | printf("%d ", d[i] == 0x3f3f3f3f ? INT_MAX : d[i]); 133 | } 134 | printf("\n"); 135 | return 0; 136 | } 137 | -------------------------------------------------------------------------------- /算法/数据结构/可持久化并查集.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | const int maxn = 210000; 8 | struct node { 9 | int lc, rc, fa, size; 10 | } t[maxn * 40]; //若内存不够,可以考虑改成按深度合并 11 | int cur, length; 12 | void init(int n) { 13 | cur = 0; 14 | length = n; 15 | } 16 | int build(int L = 1, int R = length) { 17 | int p = ++cur; 18 | t[p].fa = L; 19 | t[p].size = 1; 20 | if (L < R) { 21 | int mid = (L + R) >> 1; 22 | t[p].lc = build(L, mid); 23 | t[p].rc = build(mid + 1, R); 24 | } 25 | return p; 26 | } 27 | int link(int i, int pos, int fa, int L = 1, int R = length) { 28 | int p = ++cur; t[p] = t[i]; 29 | t[p].fa = fa; 30 | if (L < R) { 31 | int mid = (L + R) >> 1; 32 | if (pos <= mid) 33 | t[p].lc = link(t[p].lc, pos, fa, L, mid); 34 | else 35 | t[p].rc = link(t[p].rc, pos, fa, mid + 1, R); 36 | } 37 | return p; 38 | } 39 | int update(int i, int pos, int size, int L = 1, int R = length) { 40 | int p = ++cur; t[p] = t[i]; 41 | if (L == R) 42 | t[p].size += size; 43 | else { 44 | int mid = (L + R) >> 1; 45 | if (pos <= mid) 46 | t[p].lc = update(t[p].lc, pos, size, L, mid); 47 | else 48 | t[p].rc = update(t[p].rc, pos, size, mid + 1, R); 49 | } 50 | return p; 51 | } 52 | int query(int p, int pos, int L = 1, int R = length) { 53 | if (L == R) 54 | return p; 55 | else { 56 | int mid = (L + R) >> 1; 57 | if (pos <= mid) 58 | return query(t[p].lc, pos, L, mid); 59 | else 60 | return query(t[p].rc, pos, mid + 1, R); 61 | } 62 | } 63 | int find(int i, int pos) { 64 | int p = query(i, pos); 65 | return t[p].fa == pos ? p : find(i, t[p].fa); 66 | } 67 | int join(int i, int x, int y) { //在版本为i的并查集中连接点x和点y,返回新的并查集 68 | int a = find(i, x); 69 | int b = find(i, y); 70 | if (t[a].fa != t[b].fa) { //如果x和y已经在一个集合中了,则不修改直接返回 71 | if (t[a].size > t[b].size) 72 | swap(a, b); 73 | int p = link(i, t[a].fa, t[b].fa); 74 | p = update(p, t[b].fa, t[a].size); 75 | return p; 76 | } 77 | return i; 78 | } 79 | bool same(int i, int x, int y) { //判断在版本为i的并查集中,点x和点y是否属于相同的集合 80 | int a = find(i, x); 81 | int b = find(i, y); 82 | return t[a].fa == t[b].fa; 83 | } 84 | int main() { 85 | //freopen("in.txt", "r", stdin); 86 | static int root[maxn]; 87 | int n, m; 88 | scanf("%d %d", &n, &m); 89 | init(n); 90 | int r = root[0] = build(); 91 | for (int i = 1; i <= m; ++i) { 92 | int tp, x, y, k; 93 | scanf("%d", &tp); 94 | if (tp == 1) { 95 | scanf("%d %d", &x, &y); 96 | r = join(r, x, y); 97 | } 98 | else if (tp == 2) { 99 | scanf("%d", &k); 100 | r = root[k]; 101 | } 102 | else { 103 | scanf("%d %d", &x, &y); 104 | int ans = same(r, x, y); 105 | printf("%d\n", ans); 106 | } 107 | root[i] = r; 108 | } 109 | return 0; 110 | } -------------------------------------------------------------------------------- /算法/数据结构/可持久化数组.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #define lc t[p].lchild 8 | #define rc t[p].rchild 9 | template 10 | struct Array { 11 | static const int size = end - begin + 1; 12 | struct segment { 13 | int lchild, rchild; 14 | T val; 15 | }t[size * factor]; 16 | int sz; 17 | void ins(int& p, int x, int y, T A[]) { 18 | p = ++sz; 19 | if (x == y) 20 | t[p].val = A[x]; 21 | else { 22 | int mid = (x + y) >> 1; 23 | ins(lc, x, mid, A); 24 | ins(rc, mid + 1, y, A); 25 | } 26 | } 27 | //将数组A中的值作为初始值,并返回初始数组的版本号 28 | int init(T A[]) { 29 | int root = sz = 0; 30 | ins(root, begin, end, A); 31 | return root; 32 | } 33 | //将val作为可持久化数组的初始值,并返回初始数组的版本号(0) 34 | //采用此初始化方案时,数组的区间范围可以很大, 35 | //且可以在get函数中加入p==0就直接返回t[0].val的优化 36 | int init(T val) { 37 | t[0].val = val; 38 | return 0; 39 | } 40 | //获取版本号p的下标为index的值 41 | T get(int p, int index, int x = begin, int y = end) { 42 | if (x == y) 43 | return t[p].val; 44 | int mid = (x + y) >> 1; 45 | if (index <= mid) 46 | return get(lc, index, x, mid); 47 | else 48 | return get(rc, index, mid + 1, y); 49 | } 50 | //修改版本号为p的数组的下标index位置的值为v 51 | //使用方法为:root[i] = root[i-1]; set(root[i], idx, v); 52 | void set(int &p, int index, const T& v, int x = begin, int y = end) { 53 | t[++sz] = t[p]; p = sz; //如果类型T的复制开销较大,此处可以考虑只复制左右子树的编号 54 | if (x == y) 55 | t[p].val = v; 56 | else { 57 | int mid = (x + y) >> 1; 58 | if (index <= mid) 59 | set(lc, index, v, x, mid); 60 | else 61 | set(rc, index, v, mid + 1, y); 62 | } 63 | } 64 | }; 65 | 66 | const int maxn = 1000; 67 | Array arr; 68 | int A[maxn]; 69 | int main() { 70 | using namespace std; 71 | for (int i = 0; i < maxn; ++i) 72 | A[i] = -1; 73 | int p = arr.init(-1); 74 | for (int i = 0; i < maxn; ++i) 75 | if (arr.get(p, i) != A[i]) 76 | abort(); 77 | vector total[maxn]; 78 | vector root; root.push_back(p); 79 | total[0] = vector(A, A + maxn); 80 | int T = 10000; 81 | while (T--) { 82 | int tp = rand() % 10; 83 | int pos = rand() % maxn, nd = rand() % root.size(); 84 | int value = rand(); 85 | if (tp != 0) { 86 | if (arr.get(root[nd], pos) != total[nd][pos]) { 87 | abort(); 88 | } 89 | } 90 | else { 91 | int sz = root.size(); 92 | total[sz] = total[nd]; 93 | total[sz][pos] = value; 94 | root.push_back(root[nd]); 95 | arr.set(root.back(), pos, value); 96 | } 97 | } 98 | return 0; 99 | } -------------------------------------------------------------------------------- /算法/数据结构/可持久化线段树.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 可持久化线段树:区间取min 3 | 每次update之后返回新版本的线段树的根结点。 4 | 查询操作传入相应版本的线段树的根结点即可。 5 | 因为要可持久化,所以必须标记永久化,不能pushdown。 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | using namespace std; 13 | const int maxn = 110000; 14 | const int inf = 1 << 30; 15 | struct node { 16 | int l, r, lc, rc; 17 | int minv, tag; 18 | } t[maxn * 20]; 19 | int cur = 0, mn; 20 | void init() { 21 | cur = 0; 22 | } 23 | void maintain(int p) { 24 | int lc = t[p].lc, rc = t[p].rc; 25 | t[p].minv = t[p].tag; 26 | if (t[p].r > t[p].l) { 27 | t[p].minv = min({ t[lc].minv, t[rc].minv, t[p].tag }); 28 | } 29 | } 30 | int build(int L, int R) { //只要计算mid的方式是(L + R) >> 1而不是(L + R) / 2,就可以建立负坐标线段树。 31 | int p = ++cur; 32 | t[p].l = L; 33 | t[p].r = R; 34 | t[p].tag = inf; //重置结点标记 35 | if (L < R) { 36 | int mid = (L + R) >> 1; 37 | t[p].lc = build(L, mid); 38 | t[p].rc = build(mid + 1, R); 39 | } 40 | maintain(p); 41 | return p; 42 | } 43 | int update(int i, int L, int R, int v) { //区间取min 44 | int p = ++cur; t[p] = t[i]; 45 | if (L <= t[p].l && R >= t[p].r) { 46 | t[p].tag = min(t[p].tag, v); 47 | } 48 | else { 49 | int mid = (t[p].l + t[p].r) >> 1; 50 | if (L <= mid) 51 | t[p].lc = update(t[p].lc, L, R, v); 52 | if (R > mid) 53 | t[p].rc = update(t[p].rc, L, R, v); 54 | } 55 | maintain(p); 56 | return p; 57 | } 58 | void query(int p, int L, int R) { //调用之前设置mn = inf,查询区间最小值 59 | mn = min(mn, t[p].tag); 60 | if (L <= t[p].l && R >= t[p].r) { 61 | mn = min(mn, t[p].minv); 62 | } 63 | else { 64 | int mid = (t[p].l + t[p].r) >> 1; 65 | if (L <= mid) 66 | query(t[p].lc, L, R); 67 | if (R > mid) 68 | query(t[p].rc, L, R); 69 | } 70 | } 71 | int main() { 72 | init(); 73 | int root[10], n = 10; 74 | root[0] = build(1, n); 75 | root[1] = update(root[0], 1, 7, 10); 76 | root[2] = update(root[1], 5, 8, 0); 77 | mn = inf; query(root[2], 7, 7); 78 | printf("%d\n", mn); 79 | return 0; 80 | } -------------------------------------------------------------------------------- /算法/数据结构/李超线段树.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | const int maxn = 200005; 10 | const int inf = 1 << 30; 11 | int cur = 0; 12 | struct segment { 13 | int l, r, lc, rc; 14 | double k, b; 15 | }t[maxn * 4]; 16 | void init() { 17 | cur = 0; 18 | } 19 | int build(int L, int R) { //新建一棵线段树,并返回根结点的编号 20 | int p = ++cur; 21 | t[p].l = L; 22 | t[p].r = R; 23 | if (L < R) { 24 | int mid = (L + R) >> 1; 25 | t[p].lc = build(L, mid); 26 | t[p].rc = build(mid + 1, R); 27 | } 28 | t[p].k = 0; t[p].b = -inf; 29 | return p; 30 | } 31 | void pushdown(int p, double k, double b) { 32 | double l1 = k * t[p].l + b, r1 = k * t[p].r + b; 33 | double l2 = t[p].k * t[p].l + t[p].b, r2 = t[p].k * t[p].r + t[p].b; 34 | if (l1 >= l2 && r1 >= r2) 35 | t[p].k = k, t[p].b = b; 36 | else if (l2 < l1 || r2 < r1) { 37 | double pos = (b - t[p].b) / (t[p].k - k); 38 | int mid = (t[p].l + t[p].r) >> 1; 39 | if (pos <= mid) { 40 | if (r1 > r2) 41 | swap(t[p].k, k), swap(t[p].b, b); 42 | pushdown(t[p].lc, k, b); 43 | } 44 | else { 45 | if (l1 > l2) 46 | swap(t[p].k, k), swap(t[p].b, b); 47 | pushdown(t[p].rc, k, b); 48 | } 49 | } 50 | } 51 | void insert(int p, int L, int R, double k, double b) { //在区间[L, R]中插入直线y = kx + b 52 | if (L <= t[p].l && R >= t[p].r) 53 | pushdown(p, k, b); 54 | else { 55 | int mid = (t[p].l + t[p].r) >> 1; 56 | if (L <= mid) 57 | insert(t[p].lc, L, R, k, b); 58 | if (R > mid) 59 | insert(t[p].rc, L, R, k, b); 60 | } 61 | } 62 | double query(int p, int x) { //p是线段树的根结点,x是查询的横坐标,返回所有直线在x处的最大值 63 | double ans = t[p].k * x + t[p].b; 64 | if (t[p].l < t[p].r) { 65 | int mid = (t[p].l + t[p].r) >> 1; 66 | if (x <= mid) 67 | ans = max(ans, query(t[p].lc, x)); 68 | else 69 | ans = max(ans, query(t[p].rc, x)); 70 | } 71 | return ans; 72 | } 73 | 74 | const double eps = 1e-9; 75 | struct Seg { 76 | Seg() : k(), b(), id(1) {} 77 | Seg(double k, double b) : k(k), b(b) {} 78 | double k, b; 79 | int id; 80 | }A[maxn]; 81 | int main() { 82 | srand(time(0)); 83 | int n = 200000, m = 300; 84 | for (int i = 1; i <= m; ++i) 85 | A[i].k = rand() % 10 + 1, A[i].b = rand() % 1000 - 500, A[i].id = i; 86 | init(); 87 | int root = build(1, n); 88 | for (int i = 1; i <= m; ++i) 89 | insert(root, 1, n, A[i].k, A[i].b); 90 | for (int i = 1; i <= n; ++i) { 91 | long long ans = query(root, i) + eps; 92 | long long res = -inf; 93 | for (int j = 1; j <= m; ++j) 94 | res = max(res, (long long)(A[j].k * i + A[j].b + eps)); 95 | if (res != ans) 96 | printf("%lld %lld\n", ans, res); 97 | } 98 | return 0; 99 | } -------------------------------------------------------------------------------- /算法/数据结构/树状数组.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/数据结构/树状数组.cpp -------------------------------------------------------------------------------- /算法/数据结构/树链剖分.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 1. 如果权值在边上的话,则先将权值压到深度大的点上,再建立线段树。 3 | 2. 结点x的子树对应的DFS序区间为[id[x], id[x] + siz[x] - 1]。 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #define lc t[p].lchild 12 | #define rc t[p].rchild 13 | using namespace std; 14 | const int maxn = 110000; 15 | int cur, root, dfn, dep[maxn], siz[maxn], pa[maxn], id[maxn], son[maxn], top[maxn]; 16 | int val[maxn], A[maxn], ans; 17 | vector G[maxn]; 18 | struct segment { 19 | int l, r, lchild, rchild; 20 | int sum, add; 21 | }t[maxn * 4]; 22 | inline void maintain(int p) { 23 | t[p].sum = t[lc].sum + t[rc].sum; 24 | } 25 | inline void mark(int p, int addv) { //给结点打标记 26 | if (addv) { 27 | t[p].add += addv; 28 | t[p].sum += addv * (t[p].r - t[p].l + 1); 29 | } 30 | } 31 | inline void pushdown(int p) { //pushdown将标记传递给子结点,不影响当前结点的信息。 32 | mark(lc, t[p].add); 33 | mark(rc, t[p].add); 34 | t[p].add = 0; 35 | } 36 | int build(int L, int R) { 37 | int p = ++cur; 38 | t[p].l = L; 39 | t[p].r = R; 40 | t[p].add = 0; 41 | if (L == R) { 42 | mark(p, val[L]); 43 | } 44 | else { 45 | int mid = (L + R) >> 1; 46 | lc = build(L, mid); 47 | rc = build(mid + 1, R); 48 | maintain(p); 49 | } 50 | return p; 51 | } 52 | void update(int p, int L, int R, int v) { 53 | if (L <= t[p].l && R >= t[p].r) { 54 | mark(p, v); 55 | } 56 | else { 57 | pushdown(p); //如果没有pushdown只需要在最后调用一次maintain即可。 58 | int mid = (t[p].l + t[p].r) >> 1; 59 | if (L <= mid) 60 | update(lc, L, R, v); 61 | if (R > mid) 62 | update(rc, L, R, v); 63 | maintain(p); 64 | } 65 | } 66 | void query(int p, int L, int R) { 67 | if (L <= t[p].l && R >= t[p].r) { 68 | ans += t[p].sum; 69 | } 70 | else { 71 | pushdown(p); 72 | int mid = (t[p].l + t[p].r) >> 1; 73 | if (L <= mid) 74 | query(lc, L, R); 75 | if (R > mid) 76 | query(rc, L, R); 77 | } 78 | } 79 | void dfs1(int x, int fa, int d) { 80 | dep[x] = d; 81 | siz[x] = 1; 82 | son[x] = 0; 83 | pa[x] = fa; 84 | for (auto y : G[x]) if (y != fa) { 85 | dfs1(y, x, d + 1); 86 | siz[x] += siz[y]; 87 | if (siz[son[x]] < siz[y]) 88 | son[x] = y; 89 | } 90 | } 91 | void dfs2(int x, int tp) { 92 | top[x] = tp; 93 | id[x] = ++dfn; //id[x]表示结点x的DFS序编号 94 | if (son[x]) 95 | dfs2(son[x], tp); 96 | for (auto y : G[x]) 97 | if (y != pa[x] && y != son[x]) 98 | dfs2(y, y); 99 | } 100 | int ask(int x, int y) { 101 | ::ans = 0; 102 | while (top[x] != top[y]) { 103 | if (dep[top[x]] < dep[top[y]]) 104 | swap(x, y); 105 | query(root, id[top[x]], id[x]); 106 | x = pa[top[x]]; 107 | } 108 | //如果权值在边上则加上:if (x == y) return ans; 109 | if (dep[x] > dep[y]) swap(x, y); 110 | query(root, id[x], id[y]); //如果权值在边上则查询id[son[x]] 111 | return ans; 112 | } 113 | void add(int x, int y, int v) { 114 | while (top[x] != top[y]) { 115 | if (dep[top[x]] < dep[top[y]]) 116 | swap(x, y); 117 | update(root, id[top[x]], id[x], v); 118 | x = pa[top[x]]; 119 | } 120 | //如果权值在边上则加上:if (x == y) return ans; 121 | if (dep[x] > dep[y]) swap(x, y); 122 | update(root, id[x], id[y], v); //如果权值在边上则改为id[son[x]] 123 | } 124 | void init(int n) { //调用该函数之前应当先完成建图 125 | cur = dfn = siz[0] = 0; 126 | dfs1(1, -1, 1); //根结点为s的话,改为:dfs1(s, -1, 1), dfs2(s, s); 127 | dfs2(1, 1); 128 | for (int i = 1; i <= n; ++i) 129 | val[id[i]] = A[i]; //A[i]表示结点i的权值,将其复制到DFS序上。 130 | ::root = build(1, n); 131 | } 132 | int main() { 133 | return 0; 134 | } 135 | 136 | 137 | -------------------------------------------------------------------------------- /算法/数据结构/树链剖分求LCA和距离.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | const int maxn = 510000; 9 | const int inf = 1 << 30; 10 | int dep[maxn], sz[maxn], pa[maxn], son[maxn], top[maxn]; 11 | vector G[maxn]; 12 | void dfs1(int u, int fa, int d) { 13 | dep[u] = d; 14 | sz[u] = 1; 15 | son[u] = 0; 16 | pa[u] = fa; 17 | for (auto v : G[u]) if (v != fa) { 18 | dfs1(v, u, d + 1); 19 | sz[u] += sz[v]; 20 | if (sz[son[u]] < sz[v]) 21 | son[u] = v; 22 | } 23 | } 24 | void dfs2(int u, int tp) { 25 | top[u] = tp; 26 | if (son[u]) 27 | dfs2(son[u], tp); 28 | for (auto v : G[u]) if (v != pa[u] && v != son[u]) 29 | dfs2(v, v); 30 | } 31 | int lca(int x, int y) { 32 | while (top[x] != top[y]) { 33 | if (dep[top[x]] < dep[top[y]]) 34 | swap(x, y); 35 | x = pa[top[x]]; 36 | } 37 | return dep[x] < dep[y] ? x : y; 38 | } 39 | int dist(int x, int y) { 40 | return dep[x] + dep[y] - 2 * dep[lca(x, y)]; 41 | } 42 | void init(int root) { 43 | dfs1(root, 0, 1); 44 | dfs2(root, root); 45 | } 46 | int main() { 47 | int n, m, s; 48 | scanf("%d %d %d", &n, &m, &s); 49 | for (int i = 0; i < n - 1; ++i) { 50 | int x, y; 51 | scanf("%d %d", &x, &y); 52 | G[x].push_back(y); 53 | G[y].push_back(x); 54 | } 55 | init(s); 56 | while (m--) { 57 | int x, y; 58 | scanf("%d %d", &x, &y); 59 | printf("%d\n", dist(x, y)); 60 | } 61 | return 0; 62 | } -------------------------------------------------------------------------------- /算法/数据结构/线段树.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | const int maxn = 210000, offset = 210000; 10 | const int inf = 1 << 30; 11 | struct tmp { 12 | int data[maxn * 5]; 13 | int& operator[] (int idx) { 14 | return data[idx + offset]; 15 | } 16 | }A; 17 | 18 | #define lc t[p].lchild 19 | #define rc t[p].rchild 20 | int cur = 0, tot, mn, mx; 21 | struct segment { 22 | int l, r, lchild, rchild; 23 | int sum, min, max, set, add; 24 | }t[maxn * 4]; 25 | void init() { 26 | cur = 0; 27 | } 28 | inline void maintain(int p) { 29 | t[p].sum = t[lc].sum + t[rc].sum; 30 | t[p].min = min(t[lc].min, t[rc].min); 31 | t[p].max = max(t[lc].max, t[rc].max); 32 | } 33 | inline void mark(int p, int setv, int addv) { //给结点打标记 34 | if (setv >= 0) { 35 | t[p].set = setv; t[p].add = 0; 36 | t[p].min = t[p].max = setv; 37 | t[p].sum = setv * (t[p].r - t[p].l + 1); 38 | } 39 | if (addv) { 40 | t[p].add += addv; 41 | t[p].min += addv; 42 | t[p].max += addv; 43 | t[p].sum += addv * (t[p].r - t[p].l + 1); 44 | } 45 | } 46 | inline void pushdown(int p) { //pushdown将标记传递给子结点,不影响当前结点的信息。 47 | mark(lc, t[p].set, t[p].add); 48 | mark(rc, t[p].set, t[p].add); 49 | t[p].set = -1; 50 | t[p].add = 0; 51 | } 52 | int build(int L, int R) { //只要计算mid的方式是(L + R) >> 1而不是(L + R) / 2,就可以建立负坐标线段树。 53 | int p = ++cur; 54 | t[p].l = L; 55 | t[p].r = R; 56 | t[p].add = 0; t[p].set = -1; //清空结点标记 57 | if (t[p].l == t[p].r) { 58 | mark(p, 0, A[L]); 59 | } 60 | else { 61 | int mid = (t[p].l + t[p].r) >> 1; 62 | lc = build(L, mid); 63 | rc = build(mid + 1, R); 64 | maintain(p); 65 | } 66 | return p; 67 | } 68 | void update(int p, int L, int R, int op, int v) { 69 | if (L <= t[p].l && R >= t[p].r) { 70 | if (op == 0) 71 | mark(p, -1, v); 72 | else 73 | mark(p, v, 0); 74 | } 75 | else { 76 | pushdown(p); //如果没有pushdown只需要在最后调用一次maintain即可。 77 | int mid = (t[p].l + t[p].r) >> 1; 78 | if (L <= mid) 79 | update(lc, L, R, op, v); 80 | if (R > mid) 81 | update(rc, L, R, op, v); 82 | maintain(p); 83 | } 84 | } 85 | void update(int p, int pos, int v) { //单点修改 86 | if (t[p].l == t[p].r) { 87 | mark(p, -1, v); 88 | } 89 | else { 90 | pushdown(p); 91 | int mid = (t[p].l + t[p].r) >> 1; 92 | if (pos <= mid) 93 | update(lc, pos, v); 94 | else 95 | update(rc, pos, v); 96 | maintain(p); 97 | } 98 | } 99 | void query(int p, int L, int R) { //调用之前要设置:mn = inf; mx = -inf; tot = 0; 100 | if (L <= t[p].l && R >= t[p].r) { 101 | tot += t[p].sum; 102 | mn = min(mn, t[p].min); 103 | mx = max(mx, t[p].max); 104 | } 105 | else { 106 | pushdown(p); 107 | int mid = (t[p].l + t[p].r) >> 1; 108 | if (L <= mid) 109 | query(lc, L, R); 110 | if (R > mid) 111 | query(rc, L, R); 112 | } 113 | } 114 | int main() { 115 | default_random_engine e; 116 | int n = 100000, m = 100000; 117 | uniform_int_distribution d(-n, n); 118 | for (int i = -n; i <= n; ++i) 119 | A[i] = d(e); 120 | init(); 121 | int root = build(-n, n); 122 | for (int i = 1; i <= m; ++i) { 123 | int op = rand() % 4, a = d(e), b = d(e), v = rand(); 124 | int L = min(a, b), R = max(a, b); 125 | if (op == 0) { 126 | for (int i = L; i <= R; ++i) 127 | A[i] += v; 128 | update(root, L, R, op, v); 129 | } 130 | else if (op == 1) { 131 | for (int i = L; i <= R; ++i) 132 | A[i] = v; 133 | update(root, L, R, op, v); 134 | } 135 | else if (op == 2) { 136 | mn = inf; mx = -inf; tot = 0; 137 | query(root, L, R); 138 | if (mn != *min_element(A.data + offset + L, A.data + offset + R + 1)) 139 | abort(); 140 | if (mx != *max_element(A.data + offset + L, A.data + offset + R + 1)) 141 | abort(); 142 | if (tot != accumulate(A.data + offset + L, A.data + offset + R + 1, 0)) 143 | abort(); 144 | } 145 | else { 146 | A[L] += v; 147 | update(root, L, v); 148 | } 149 | } 150 | return 0; 151 | } -------------------------------------------------------------------------------- /算法/数据结构/线段树(历史最大值).cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 1. 标记(a, b)表示对区间中的所有值v执行:v = max(v + a, b) 3 | 2. 将标记(c, d)合并到(a, b)上得到:(a + c, max(b + c, d)) 4 | 3. 标记(a, b)与(c, d)取最大得到:(max(a, c), max(b, d)) 5 | 4. 必须满足inf > 操作次数 * 单次修改的最大增加值 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #define lc t[p].lchild 13 | #define rc t[p].rchild 14 | using namespace std; 15 | const int maxn = 510000; 16 | const long long inf = 1LL << 50; 17 | long long A[maxn], mx, hmax; 18 | int cur = 0; 19 | struct segment { 20 | int l, r, lchild, rchild; 21 | long long max, hmax; //最大值、历史最大值 22 | long long a, b, x, y; //(a, b)为当前标记,(x, y)为历史最大标记 23 | }t[maxn * 4]; 24 | inline void maintain(int p) { 25 | t[p].max = max(t[lc].max, t[rc].max); 26 | t[p].hmax = max(t[p].hmax, t[p].max); 27 | } 28 | inline void mark(int p, long long c, long long d) { 29 | t[p].a += c; 30 | t[p].b = max(t[p].b + c, d); 31 | t[p].max = max(t[p].max + c, d); 32 | t[p].x = max(t[p].x, t[p].a); 33 | t[p].y = max(t[p].y, t[p].b); 34 | t[p].hmax = max(t[p].hmax, t[p].max); 35 | t[p].a = max(t[p].a, -inf); //防止出现:(操作次数 * inf) > LLONG_MAX 36 | } 37 | inline void change(int p, long long c, long long d) { 38 | long long i = t[p].a + c, j = max(t[p].b + c, d); 39 | t[p].x = max(t[p].x, i); 40 | t[p].y = max(t[p].y, j); 41 | t[p].hmax = max(t[p].hmax, max(t[p].max + c, d)); 42 | } 43 | inline void pushdown(int p) { 44 | change(lc, t[p].x, t[p].y); 45 | change(rc, t[p].x, t[p].y); 46 | mark(lc, t[p].a, t[p].b); 47 | mark(rc, t[p].a, t[p].b); 48 | t[p].x = t[p].a = 0; 49 | t[p].y = t[p].b = -inf; 50 | } 51 | int build(int L, int R) { 52 | int p = ++cur; 53 | t[p].l = L; 54 | t[p].r = R; 55 | t[p].x = t[p].a = 0; 56 | t[p].y = t[p].b = -inf; 57 | t[p].hmax = -inf; 58 | if (t[p].l == t[p].r) { 59 | t[p].hmax = t[p].max = A[L]; 60 | } 61 | else { 62 | int mid = (t[p].l + t[p].r) >> 1; 63 | lc = build(L, mid); 64 | rc = build(mid + 1, R); 65 | maintain(p); 66 | } 67 | return p; 68 | } 69 | void query(int p, int L, int R) { 70 | if (L <= t[p].l && R >= t[p].r) { 71 | mx = max(mx, t[p].max); 72 | hmax = max(hmax, t[p].hmax); 73 | } 74 | else { 75 | pushdown(p); 76 | int mid = (t[p].l + t[p].r) >> 1; 77 | if (L <= mid) 78 | query(lc, L, R); 79 | if (R > mid) 80 | query(rc, L, R); 81 | } 82 | } 83 | void update(int p, int L, int R, long long a, long long b) { 84 | if (L <= t[p].l && R >= t[p].r) { 85 | mark(p, a, b); 86 | } 87 | else { 88 | pushdown(p); 89 | int mid = (t[p].l + t[p].r) >> 1; 90 | if (L <= mid) 91 | update(lc, L, R, a, b); 92 | if (R > mid) 93 | update(rc, L, R, a, b); 94 | maintain(p); 95 | } 96 | } 97 | int main() { 98 | //freopen("in.txt", "r", stdin); 99 | int n, m; 100 | scanf("%d", &n); 101 | for (int i = 1; i <= n; ++i) 102 | scanf("%lld", &A[i]); 103 | int root = build(1, n); 104 | scanf("%d", &m); 105 | while (m--) { 106 | char tp; 107 | int L, R; 108 | long long v; 109 | scanf(" %c %d %d\n", &tp, &L, &R); 110 | if (tp == 'Q') { //查询区间最大值 111 | mx = -inf; 112 | query(root, L, R); 113 | printf("%lld\n", mx); 114 | } 115 | else if (tp == 'A') { //查询区间历史最大值 116 | hmax = -inf; 117 | query(root, L, R); 118 | printf("%lld\n", hmax); 119 | } 120 | else if (tp == 'P') { //区间add 121 | scanf("%lld", &v); 122 | update(root, L, R, v, -inf); 123 | } 124 | else { //区间set 125 | scanf("%lld", &v); 126 | update(root, L, R, -inf, v); 127 | } 128 | } 129 | return 0; 130 | } -------------------------------------------------------------------------------- /算法/树算法/O(1)-LCA.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | const int maxn = 1110000; //maxn至少为最大结点数的两倍 9 | vector G[maxn], seq; 10 | int pos[maxn], dep[maxn], lg[maxn], a[maxn][20]; 11 | void dfs(int x, int fa, int d) { 12 | dep[x] = d; 13 | pos[x] = seq.size(); 14 | seq.push_back(x); 15 | for (auto y : G[x]) if (y != fa) { 16 | dfs(y, x, d + 1); 17 | seq.push_back(x); 18 | } 19 | } 20 | void init(int s) { //根结点为s,调用之前G中应当已经保存了整棵树。 21 | seq.resize(1); 22 | dfs(s, -1, 1); 23 | const int n = seq.size() - 1; 24 | lg[1] = 0; 25 | for (int i = 2; i <= n; ++i) 26 | lg[i] = lg[i >> 1] + 1; 27 | for (int i = 1; i <= n; ++i) 28 | a[i][0] = seq[i]; 29 | for (int j = 1; j <= lg[n]; ++j) { 30 | for (int i = 1; i + (1 << j) - 1 <= n; ++i) { 31 | int x = a[i][j - 1], y = a[i + (1 << (j - 1))][j - 1]; 32 | a[i][j] = (dep[x] < dep[y] ? x : y); 33 | } 34 | } 35 | } 36 | inline int lca(int x, int y) { 37 | int L = pos[x], R = pos[y]; 38 | if (L > R) 39 | swap(L, R); 40 | int k = lg[R - L + 1]; 41 | x = a[L][k]; 42 | y = a[R - (1 << k) + 1][k]; 43 | return dep[x] < dep[y] ? x : y; 44 | } 45 | inline int dist(int x, int y) { 46 | return dep[x] + dep[y] - 2 * dep[lca(x, y)]; 47 | } 48 | int main() { 49 | //freopen("in.txt", "r", stdin); 50 | int n, m, s; 51 | scanf("%d %d %d", &n, &m, &s); 52 | for (int i = 1; i < n; ++i) { 53 | int x, y; 54 | scanf("%d %d", &x, &y); 55 | G[x].push_back(y); 56 | G[y].push_back(x); 57 | } 58 | init(s); 59 | for (int i = 0; i < m; ++i) { 60 | int x, y; 61 | scanf("%d %d", &x, &y); 62 | printf("%d\n", lca(x, y)); 63 | } 64 | return 0; 65 | } -------------------------------------------------------------------------------- /算法/树算法/倍增求LCA.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | const int maxn = 510000; 9 | const int maxlog = 20; 10 | vector G[maxn]; 11 | int anc[maxn][maxlog], dep[maxn]; 12 | void dfs(int x, int fa, int d) { 13 | anc[x][0] = fa; 14 | dep[x] = d; 15 | for (auto y : G[x]) if (y != fa) 16 | dfs(y, x, d + 1); 17 | } 18 | void preprocess(int n) { //点的编号从1开始 19 | for (int j = 1; j < maxlog; ++j) 20 | for (int i = 0; i <= n; ++i) 21 | anc[i][j] = 0; 22 | dfs(1, 0, 0); 23 | for (int j = 1; j < maxlog; ++j) 24 | for (int i = 1; i <= n; ++i) 25 | anc[i][j] = anc[anc[i][j - 1]][j - 1]; 26 | } 27 | //返回结点x向上走d步到达的结点 28 | int moveup(int x, int d) { 29 | for (int i = 0; d >> i; ++i) 30 | if (d >> i & 1) 31 | x = anc[x][i]; 32 | return x; 33 | } 34 | int lca(int x, int y) { 35 | if (dep[x] < dep[y]) 36 | swap(x, y); 37 | x = moveup(x, dep[x] - dep[y]); 38 | if (x == y) 39 | return x; 40 | for (int i = maxlog - 1; i >= 0; --i) 41 | if (anc[x][i] != anc[y][i]) 42 | x = anc[x][i], y = anc[y][i]; 43 | return anc[x][0]; 44 | } 45 | int dist(int x, int y) { //返回结点x和y之间的距离 46 | return dep[x] + dep[y] - 2 * dep[lca(x, y)]; 47 | } 48 | int move(int x, int y, int d) { //返回从结点x向结点y走d步到达的结点 49 | int p = lca(x, y); 50 | int h = dep[x] - dep[p]; 51 | if (h >= d) 52 | return moveup(x, d); 53 | else 54 | return moveup(y, dep[x] + dep[y] - d - 2 * dep[p]); 55 | } 56 | int main() { 57 | //freopen("in.txt", "r", stdin); 58 | int n, m, s; 59 | scanf("%d %d %d", &n, &m, &s); 60 | for (int i = 1; i < n; ++i) { 61 | int x, y; 62 | scanf("%d %d", &x, &y); 63 | G[x].push_back(y); 64 | G[y].push_back(x); 65 | } 66 | dfs(s, 0, 0); 67 | for (int j = 1; j < maxlog; ++j) 68 | for (int i = 1; i <= n; ++i) 69 | anc[i][j] = anc[anc[i][j - 1]][j - 1]; 70 | for (int i = 0; i < m; ++i) { 71 | int x, y; 72 | scanf("%d %d", &x, &y); 73 | printf("%d\n", lca(x, y)); 74 | } 75 | return 0; 76 | } -------------------------------------------------------------------------------- /算法/树算法/动态点分治.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #define dfs(x, fa, d) 8 | using namespace std; 9 | const int maxn = 110000; 10 | vector> G[maxn]; //to, weight 11 | int vis[maxn], siz[maxn], f[maxn], pa[maxn], pos, sum; //pa[x]表示在点分树上结点x的父亲 12 | int son[maxn], dep[maxn], father[maxn], top[maxn]; 13 | void DFS1(int x, int fa, int d) { 14 | dep[x] = d; 15 | siz[x] = 1; 16 | son[x] = 0; 17 | father[x] = fa; 18 | for (auto [y, w] : G[x]) if (y != fa) { 19 | DFS1(y, x, d + 1); 20 | siz[x] += siz[y]; 21 | if (siz[son[x]] < siz[y]) 22 | son[x] = y; 23 | } 24 | } 25 | void DFS2(int x, int tp) { 26 | top[x] = tp; 27 | if (son[x]) 28 | DFS2(son[x], tp); 29 | for (auto [y, w] : G[x]) 30 | if (y != father[x] && y != son[x]) 31 | DFS2(y, y); 32 | } 33 | int lca(int x, int y) { 34 | while (top[x] != top[y]) { 35 | if (dep[top[x]] < dep[top[y]]) 36 | swap(x, y); 37 | x = father[top[x]]; 38 | } 39 | return dep[x] < dep[y] ? x : y; 40 | } 41 | int dist(int x, int y) { 42 | return dep[x] + dep[y] - 2 * dep[lca(x, y)]; 43 | } 44 | void init(int n) { 45 | for (int i = 0; i <= n; ++i) { 46 | vis[i] = false; 47 | G[i].clear(); 48 | } 49 | } 50 | void getroot(int x, int fa) { 51 | f[x] = 0; siz[x] = 1; 52 | for (auto [y, w] : G[x]) if (y != fa && !vis[y]) { 53 | getroot(y, x); 54 | f[x] = max(f[x], siz[y]); 55 | siz[x] += siz[y]; 56 | } 57 | f[x] = max(f[x], sum - siz[x]); 58 | if (f[x] < f[pos]) 59 | pos = x; 60 | } 61 | void prepare(int x) { //pa[x]为结点x在点分树中的父结点,没有的话为-1。 62 | for (auto [y, w] : G[x]) if (!vis[y]) { 63 | //注意在dfs中枚举子结点y的时候要判两个条件:y != fa && !vis[y] 64 | dfs(y, x, w); 65 | } 66 | } 67 | void solve(int x, int cnt, int pre = -1) { //cnt为子树x中的结点总数 68 | pos = maxn - 1; f[pos] = sum = cnt; 69 | getroot(x, -1); 70 | int root = pos; //因为pos是全局变量,递归的时候值会改变,所以此处存为局部变量。 71 | vis[root] = true; 72 | pa[root] = pre; 73 | for (auto [y, w] : G[root]) if (!vis[y]) { 74 | int total = siz[y] < siz[root] ? siz[y] : cnt - siz[root]; 75 | solve(y, total, root); 76 | } 77 | vis[root] = false; 78 | prepare(root); 79 | } 80 | int main() { 81 | int n; 82 | scanf("%d", &n); 83 | init(n); 84 | for (int i = 1; i < n; ++i) { 85 | int x, y, w; 86 | scanf("%d %d %d", &x, &y, &w); 87 | G[x].emplace_back(y, w); 88 | G[y].emplace_back(x, w); 89 | } 90 | 91 | DFS1(1, 0, 1); 92 | DFS2(1, 1); 93 | solve(1, n); 94 | 95 | return 0; 96 | } -------------------------------------------------------------------------------- /算法/树算法/快速求树中与结点x距离不超过k的点权和.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/树算法/快速求树中与结点x距离不超过k的点权和.cpp -------------------------------------------------------------------------------- /算法/树算法/树的欧拉序.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | seq下标从1开始,表示欧拉序列。欧拉序列记录的是点的标号,一段欧拉序列中若一个点出现了两次则不予处理。 3 | 树上的一条路径一定对应一段连续的欧拉序列(可能还要并上一个额外的点)。 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | const int maxn = 210000; 12 | const int maxlog = 25; 13 | int head[maxn], nxt[maxn], to[maxn], seq[maxn], depth[maxn], anc[maxn][maxlog], first[maxn], last[maxn], cur, sz; 14 | struct item { //下标区间[L, R]的欧拉序列,若lca != 0则还要将lca考虑进来 15 | int L, R, lca; 16 | item(){} 17 | item(int L, int R, int lca) : L(L), R(R), lca(lca){} 18 | }; 19 | void init() { 20 | cur = sz = 0; 21 | memset(head, 0, sizeof(head)); 22 | memset(depth, 0, sizeof(depth)); 23 | memset(anc, 0, sizeof(anc)); 24 | } 25 | void add_edge(int x, int y) { //对于树中的每条边要调用两次add_edge 26 | to[++cur] = y; 27 | nxt[cur] = head[x]; 28 | head[x] = cur; 29 | } 30 | void dfs(int x, int fa) { //dfs(1, 0) 31 | seq[++sz] = x; 32 | first[x] = sz; 33 | for (int i = head[x]; i; i = nxt[i]) if (to[i] != fa) { 34 | int y = to[i]; 35 | depth[y] = depth[x] + 1; 36 | anc[y][0] = x; 37 | for (int i = 1; (1 << i) <= depth[y]; ++i) 38 | anc[y][i] = anc[anc[y][i - 1]][i - 1]; 39 | dfs(y, x); 40 | } 41 | seq[++sz] = x; 42 | last[x] = sz; 43 | } 44 | int getlca(int x, int y) { 45 | if (depth[x] < depth[y]) 46 | swap(x, y); 47 | for (int i = maxlog - 1; i >= 0; --i) 48 | if (depth[x] - (1 << i) >= depth[y]) 49 | x = anc[x][i]; 50 | if (x == y) 51 | return x; 52 | for (int i = maxlog - 1; i >= 0; --i) 53 | if (anc[x][i] != anc[y][i]) 54 | x = anc[x][i], y = anc[y][i]; 55 | return anc[x][0]; 56 | } 57 | item path(int x, int y) { //返回树上x到y的路径对应的欧拉序列 58 | if (first[x] > first[y]) 59 | swap(x, y); 60 | int lca = getlca(x, y); 61 | if (lca == x) 62 | return item(first[x], first[y], 0); 63 | else 64 | return item(last[x], first[y], lca); 65 | } 66 | int main() { 67 | 68 | return 0; 69 | } -------------------------------------------------------------------------------- /算法/树算法/点分治.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #define dfs1(x, fa, d) 8 | #define dfs2(x, fa, d) 9 | using namespace std; 10 | const int maxn = 110000; 11 | vector> G[maxn]; //to, weight 12 | int vis[maxn], siz[maxn], f[maxn], pos, sum; 13 | void init(int n) { 14 | for (int i = 0; i <= n; ++i) { 15 | vis[i] = false; 16 | G[i].clear(); 17 | } 18 | } 19 | void getroot(int x, int fa) { 20 | f[x] = 0; siz[x] = 1; 21 | for (auto[y, w] : G[x]) if (y != fa && !vis[y]) { 22 | getroot(y, x); 23 | f[x] = max(f[x], siz[y]); 24 | siz[x] += siz[y]; 25 | } 26 | f[x] = max(f[x], sum - siz[x]); 27 | if (f[x] < f[pos]) 28 | pos = x; 29 | } 30 | void calc(int x) { //统计经过结点x的所有答案 31 | for (auto [y, w] : G[x]) if (!vis[y]) { 32 | //注意在dfs中枚举子结点y的时候要判两个条件:y != fa && !vis[y] 33 | dfs1(y, x, w); //计算当前子树与之前子树以及 结点x 连接构成的路径。 34 | dfs2(y, x, w); //维护处理过的子树的信息 35 | } 36 | //维护信息的时候有两种方案: 37 | //1. 在每次calc调用的时候新建一个数据结构维护。 38 | //2. 维护一个全局的数据结构,此时在calc末尾应当加一个循环来撤销之前的所有操作。 39 | } 40 | void solve(int x, int cnt) { //cnt为子树x中的结点总数 41 | pos = maxn - 1; f[pos] = sum = cnt; 42 | getroot(x, -1); 43 | int root = pos; //因为pos是全局变量,递归的时候值会改变,所以此处存为局部变量。 44 | vis[root] = 1; 45 | calc(root); 46 | for (auto [y, w] : G[root]) if (!vis[y]) 47 | solve(y, siz[y] < siz[root] ? siz[y] : cnt - siz[root]); 48 | } 49 | int main() { 50 | int n; 51 | scanf("%d", &n); 52 | init(n); 53 | for (int i = 1; i < n; ++i) { 54 | int x, y, w; 55 | scanf("%d %d %d", &x, &y, &w); 56 | G[x].emplace_back(y, w); 57 | G[y].emplace_back(x, w); 58 | } 59 | solve(1, n); 60 | return 0; 61 | } -------------------------------------------------------------------------------- /算法/树算法/虚树.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ins函数会在每个结点i第一次入栈的时候,将E[i]清空。这样在最坏情况下空间复杂度可达O(nlogn), 3 | 可以改成在每次询问后用一个DFS清空E,这样也E就不会占用额外内存了。 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | using namespace std; 12 | const int maxn = 1110000; 13 | int dep[maxn], sz[maxn], pa[maxn], son[maxn], top[maxn], stk[maxn], dfn[maxn], clk, tp; 14 | vector G[maxn], E[maxn]; 15 | void dfs1(int u, int fa, int d) { 16 | dfn[u] = ++clk; 17 | dep[u] = d; 18 | sz[u] = 1; 19 | son[u] = 0; 20 | pa[u] = fa; 21 | for (auto v : G[u]) if (v != fa) { 22 | dfs1(v, u, d + 1); 23 | sz[u] += sz[v]; 24 | if (sz[son[u]] < sz[v]) 25 | son[u] = v; 26 | } 27 | } 28 | void dfs2(int u, int tp) { 29 | top[u] = tp; 30 | if (son[u]) 31 | dfs2(son[u], tp); 32 | for (auto v : G[u]) if (v != pa[u] && v != son[u]) 33 | dfs2(v, v); 34 | } 35 | int lca(int x, int y) { 36 | while (top[x] != top[y]) { 37 | if (dep[top[x]] < dep[top[y]]) 38 | swap(x, y); 39 | x = pa[top[x]]; 40 | } 41 | return dep[x] < dep[y] ? x : y; 42 | } 43 | int dist(int x, int y) { 44 | return dep[x] + dep[y] - 2 * dep[lca(x, y)]; 45 | } 46 | void init(int root) { 47 | clk = 0; stk[0] = -1; 48 | dfs1(root, 0, 1); 49 | dfs2(root, root); 50 | } 51 | void ins(int x) { 52 | if (tp > 0) { 53 | int fa = lca(stk[tp], x); 54 | if (fa != stk[tp]) { 55 | while (tp > 1 && dep[fa] < dep[stk[tp - 1]]) { 56 | E[stk[tp - 1]].push_back(stk[tp]); 57 | --tp; 58 | } 59 | int last = stk[tp--]; 60 | if (fa != stk[tp]) { 61 | E[fa].clear(); 62 | stk[++tp] = fa; 63 | } 64 | E[fa].push_back(last); 65 | } 66 | } 67 | E[x].clear(); 68 | stk[++tp] = x; 69 | } 70 | int build(vector nodes) { //对集合nodes中的结点建立虚树,并返回根结点。 71 | sort(nodes.begin(), nodes.end(), [](int x, int y) { 72 | return dfn[x] < dfn[y]; 73 | }); 74 | for (auto i : nodes) 75 | ins(i); 76 | while (--tp) 77 | E[stk[tp]].push_back(stk[tp + 1]); 78 | return stk[1]; 79 | } 80 | 81 | int main() { 82 | return 0; 83 | } -------------------------------------------------------------------------------- /算法/树算法/限定距离的子树问题.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 该算法用来求解有根树中结点v的子树中与v距离不超过d的所有结点的权值和(也可以求最值等), 3 | 时间复杂度O(d),若用倍增可以优化到O(logd)。 4 | 调用init函数之前应当输入n(结点标号从1开始),以及用add_edge建图。 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | using namespace std; 14 | const int maxn = 1010000; 15 | int n, q, w[maxn], head[maxn], nxt[maxn * 2], to[maxn * 2], id[maxn], l[maxn], r[maxn]; 16 | int father[maxn], depth[maxn], seq[maxn], cur = 0; 17 | long long sum[maxn]; 18 | void add_edge(int u, int v) { 19 | to[++cur] = v; 20 | nxt[cur] = head[u]; 21 | head[u] = cur; 22 | } 23 | void init() { 24 | int sz = 0; 25 | queue Q; 26 | father[1] = depth[1] = seq[n + 1] = 0; 27 | Q.push(1); 28 | while (!Q.empty()) { 29 | int x = Q.front(); Q.pop(); 30 | seq[++sz] = x; 31 | id[x] = sz; 32 | l[x] = r[x] = 0; 33 | for (int i = head[x]; i; i = nxt[i]) if (to[i] != father[x]) { 34 | int y = to[i]; 35 | l[x] = (l[x] ? l[x] : y); 36 | r[x] = y; 37 | father[y] = x; 38 | depth[y] = depth[x] + 1; 39 | Q.push(y); 40 | } 41 | } 42 | for (int j = 1; j <= n; ++j) 43 | sum[j] = sum[j - 1] + w[seq[j]]; 44 | for (int j = 1; j <= n; ++j) if (!r[seq[j]]) 45 | r[seq[j]] = r[seq[j - 1]]; 46 | for (int j = n; j >= 1; --j) if (!l[seq[j]]) 47 | l[seq[j]] = l[seq[j + 1]]; 48 | } 49 | long long travel(int x, int d) { //求出以x为根的子树中与x距离不超过d的所有结点的权值和(可改为求最值或种类数) 50 | if (d < 0) return 0; //时间复杂度O(d),可以改成倍增使得时间复杂度变为O(logd) 51 | long long ret = 0; 52 | int L = x, R = x; 53 | for (int i = 0; i <= d; ++i) { 54 | int left = id[L], right = id[R]; 55 | if (L == 0 || R == 0 || left > right) break; 56 | long long s = sum[right] - sum[left - 1]; 57 | ret += s; 58 | L = l[L]; 59 | R = r[R]; 60 | } 61 | return ret; 62 | } 63 | 64 | int main() { 65 | //freopen("in.txt", "r", stdin); 66 | scanf("%d", &n); 67 | for (int i = 1; i <= n; ++i) 68 | scanf("%d", &w[i]); 69 | for (int i = 0; i < n - 1; ++i) { 70 | int u, v; 71 | scanf("%d %d", &u, &v); 72 | add_edge(u, v); 73 | add_edge(v, u); 74 | } 75 | init(); 76 | scanf("%d", &q); 77 | while (q--) { //每次询问求出树中与v距离不超过k的所有点的权值和,复杂度O(k^2) 78 | int v, k; 79 | scanf("%d %d", &v, &k); 80 | long long ans = travel(v, k); 81 | int last = v; 82 | for (int u = father[v], t = k - 1; u != 0 && t >= 0; u = father[u], --t) { 83 | ans += travel(u, t) - travel(last, t - 1); 84 | last = u; 85 | } 86 | printf("%lld\n", ans); 87 | } 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /算法/网络流/Dinic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | const int maxn = 100000 + 10; 10 | const int inf = 1 << 30; 11 | struct edge{ 12 | int from, to, cap, flow; 13 | edge(int u, int v, int c, int f) : from(u), to(v), cap(c), flow(f) {} 14 | }; 15 | struct Dinic { 16 | int n, m, s, t; 17 | vector edges; // 边数的两倍 18 | vector G[maxn]; // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号 19 | bool vis[maxn]; // BFS使用 20 | int d[maxn]; // 从起点到i的距离 21 | int cur[maxn]; // 当前弧指针 22 | void init(int n) { 23 | this->n = n; 24 | for (int i = 0; i < n; i++) 25 | G[i].clear(); 26 | edges.clear(); 27 | } 28 | void clear() { 29 | for (int i = 0; i < edges.size(); i++) 30 | edges[i].flow = 0; 31 | } 32 | void reduce() { 33 | for (int i = 0; i < edges.size(); i++) 34 | edges[i].cap -= edges[i].flow; 35 | } 36 | void addedge(int from, int to, int cap) { 37 | edges.push_back(edge(from, to, cap, 0)); 38 | edges.push_back(edge(to, from, 0, 0)); 39 | m = edges.size(); 40 | G[from].push_back(m - 2); 41 | G[to].push_back(m - 1); 42 | } 43 | bool BFS() { 44 | memset(vis, 0, sizeof(vis)); 45 | queue Q; 46 | Q.push(s); 47 | vis[s] = 1; 48 | d[s] = 0; 49 | while (!Q.empty()) { 50 | int x = Q.front(); Q.pop(); 51 | for (int i = 0; i < G[x].size(); i++) { 52 | edge& e = edges[G[x][i]]; 53 | if (!vis[e.to] && e.cap > e.flow) { 54 | vis[e.to] = 1; 55 | d[e.to] = d[x] + 1; 56 | Q.push(e.to); 57 | } 58 | } 59 | } 60 | return vis[t]; 61 | } 62 | int DFS(int x, int a) { 63 | if (x == t || a == 0) return a; 64 | int flow = 0, f; 65 | for (int& i = cur[x]; i < G[x].size(); i++) { 66 | edge& e = edges[G[x][i]]; 67 | if (d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0) { 68 | e.flow += f; 69 | edges[G[x][i] ^ 1].flow -= f; 70 | flow += f; 71 | a -= f; 72 | if (a == 0) break; 73 | } 74 | } 75 | return flow; 76 | } 77 | int Maxflow(int s, int t) { 78 | this->s = s; this->t = t; 79 | int flow = 0; 80 | while (BFS()) { 81 | memset(cur, 0, sizeof(cur)); 82 | flow += DFS(s, inf); 83 | } 84 | return flow; 85 | } 86 | vector Mincut() { // call this after maxflow 87 | vector ans; 88 | for (int i = 0; i < edges.size(); i++) { 89 | edge& e = edges[i]; 90 | if (vis[e.from] && !vis[e.to] && e.cap > 0) 91 | ans.push_back(i); 92 | } 93 | return ans; 94 | } 95 | }dinic; 96 | int main() 97 | { 98 | freopen("D:\\in.txt", "r", stdin); 99 | int n, m; 100 | scanf("%d %d", &n, &m); 101 | dinic.init(n + 5); 102 | while (m--) { 103 | int s, t, u; 104 | scanf("%d %d %d", &s, &t, &u); 105 | dinic.addedge(s, t, u); 106 | } 107 | auto start = clock(); 108 | printf("%d\n", dinic.Maxflow(1, n)); 109 | double tot = static_cast(clock() - start) / CLOCKS_PER_SEC; 110 | printf("Dinic: %f\n", tot); 111 | return 0; 112 | } 113 | -------------------------------------------------------------------------------- /算法/网络流/HLPP.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | const int maxn = 2e5 + 5, maxedges = 4e6 + 5, inf = 0x3f3f3f3f; 8 | int n, m, s, t, tot; 9 | int v[maxedges * 2], w[maxedges * 2], first[maxn], nxt[maxedges * 2]; 10 | int h[maxn], e[maxn], gap[maxn * 2], inq[maxn];//节点高度是可以到达2n-1的 11 | struct cmp{ 12 | inline bool operator()(int a, int b) const { 13 | return h[a] < h[b];//因为在优先队列中的节点高度不会改变,所以可以直接比较 14 | } 15 | }; 16 | queue Q; 17 | priority_queue, cmp> heap; 18 | inline void add_edge(int from, int to, int flow) { 19 | tot += 2; 20 | v[tot + 1] = from; v[tot] = to; w[tot] = flow; w[tot + 1] = 0; 21 | nxt[tot] = first[from]; first[from] = tot; 22 | nxt[tot + 1] = first[to]; first[to] = tot + 1; 23 | return; 24 | } 25 | inline bool bfs() { 26 | memset(h + 1, 0x3f, sizeof(int) * n); 27 | h[t] = 0; 28 | Q.push(t); 29 | while (!Q.empty()) 30 | { 31 | int now = Q.front(); Q.pop(); 32 | for (int go = first[now]; go; go = nxt[go]) 33 | if (w[go ^ 1] && h[v[go]] > h[now] + 1) 34 | h[v[go]] = h[now] + 1, Q.push(v[go]); 35 | } 36 | return h[s] != inf; 37 | } 38 | inline void push(int now) { 39 | for (int go = first[now]; go; go = nxt[go]) { 40 | if (w[go] && h[v[go]] + 1 == h[now]) { 41 | int d = min(e[now], w[go]); 42 | w[go] -= d; w[go ^ 1] += d; e[now] -= d; e[v[go]] += d; 43 | if (v[go] != s && v[go] != t && !inq[v[go]]) 44 | heap.push(v[go]), inq[v[go]] = 1; 45 | if (!e[now])//已经推送完毕可以直接退出 46 | break; 47 | } 48 | } 49 | } 50 | inline void relabel(int now) { 51 | h[now] = inf; 52 | for (int go = first[now]; go; go = nxt[go]) 53 | if (w[go] && h[v[go]] + 1 < h[now]) 54 | h[now] = h[v[go]] + 1; 55 | return; 56 | } 57 | inline int hlpp() { 58 | int now, d; 59 | if (!bfs()) //s和t不连通 60 | return 0; 61 | h[s] = n; 62 | memset(gap, 0, sizeof(int) * (n * 2)); 63 | for (int i = 1; i <= n; i++) 64 | if (h[i] < inf) 65 | ++gap[h[i]]; 66 | for (int go = first[s]; go; go = nxt[go]) { 67 | if (d = w[go]) { 68 | w[go] -= d; w[go ^ 1] += d; e[s] -= d; e[v[go]] += d; 69 | if (v[go] != s && v[go] != t && !inq[v[go]]) 70 | heap.push(v[go]), inq[v[go]] = 1; 71 | } 72 | } 73 | while (!heap.empty()) { 74 | inq[now = heap.top()] = 0; heap.pop(); push(now); 75 | if (e[now]) { 76 | if (!--gap[h[now]]) //gap优化,因为当前节点是最高的所以修改的节点一定不在优先队列中,不必担心修改对优先队列会造成影响 77 | for (int i = 1; i <= n; i++) 78 | if (i != s && i != t && h[i] > h[now] && h[i] < n + 1) 79 | h[i] = n + 1; 80 | relabel(now); ++gap[h[now]]; 81 | heap.push(now); inq[now] = 1; 82 | } 83 | } 84 | return e[t]; 85 | } 86 | int main() 87 | { 88 | freopen("D:\\in.txt", "r", stdin); 89 | scanf("%d %d", &n, &m); s = 1; t = n; 90 | while (m--) 91 | { 92 | int s, t, u; 93 | scanf("%d %d %d", &s, &t, &u); 94 | add_edge(s, t, u); 95 | } 96 | auto start = clock(); 97 | printf("%d\n", hlpp()); 98 | double tot = static_cast(clock() - start) / CLOCKS_PER_SEC; 99 | printf("HLPP: %f\n", tot); 100 | return 0; 101 | } -------------------------------------------------------------------------------- /算法/网络流/ISAP.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | const int maxn = 1100; 10 | const int maxedges = 51000; 11 | const int inf = 1 << 30; 12 | struct edge { 13 | int to, flow; 14 | edge *next, *pair; 15 | edge() {} 16 | edge(int to, int flow, edge *next) : to(to), flow(flow), next(next) {} 17 | void *operator new(unsigned, void *p) { return p; } 18 | }; 19 | struct ISAP { 20 | int gap[maxn], h[maxn], n, s, t; 21 | edge *cur[maxn], *first[maxn], edges[maxedges * 2], *ptr; 22 | void init(int n) { 23 | this->n = n; 24 | ptr = edges; 25 | memset(first, 0, sizeof(first)); 26 | memset(gap, 0, sizeof(gap)); 27 | memset(h, 0, sizeof(h)); 28 | gap[0] = n; 29 | } 30 | void add_edge(int from, int to, int cap) { 31 | first[from] = new(ptr++)edge(to, cap, first[from]); 32 | first[to] = new(ptr++)edge(from, 0, first[to]); 33 | first[from]->pair = first[to]; 34 | first[to]->pair = first[from]; 35 | } 36 | int augment(int x, int limit) { 37 | if (x == t) 38 | return limit; 39 | int rest = limit; 40 | for (edge*& e = cur[x]; e; e = e->next) if (e->flow && h[e->to] + 1 == h[x]) { 41 | int d = augment(e->to, min(rest, e->flow)); 42 | e->flow -= d, e->pair->flow += d, rest -= d; 43 | if (h[s] == n || !rest) 44 | return limit - rest; 45 | } 46 | int minh = n; 47 | for (edge *e = cur[x] = first[x]; e; e = e->next) if (e->flow) 48 | minh = min(minh, h[e->to] + 1); 49 | if (--gap[h[x]] == 0) 50 | h[s] = n; 51 | else 52 | ++gap[h[x] = minh]; 53 | return limit - rest; 54 | } 55 | int solve(int s, int t, int limit = inf) { 56 | this->s = s; this->t = t; 57 | memcpy(cur, first, sizeof(first)); // memcpy! 58 | int flow = 0; 59 | while (h[s] < n && flow < limit) 60 | flow += augment(s, limit - flow); 61 | return flow; 62 | } 63 | }isap; 64 | int main() 65 | { 66 | freopen("D:\\in.txt", "r", stdin); 67 | int n, m; 68 | scanf("%d %d", &n, &m); 69 | isap.init(n + 5); 70 | while (m--) 71 | { 72 | int s, t, u; 73 | scanf("%d %d %d", &s, &t, &u); 74 | isap.add_edge(s, t, u); 75 | } 76 | auto start = clock(); 77 | printf("%d\n", isap.solve(1, n)); 78 | double tot = static_cast(clock() - start) / CLOCKS_PER_SEC; 79 | printf("Isap: %f\n", tot); 80 | return 0; 81 | } -------------------------------------------------------------------------------- /算法/网络流/MCMF-dijkstra.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | const int maxn = 21000; 10 | const int inf = 1 << 30; 11 | struct edge { 12 | int to, cap, cost, rev; 13 | edge() {} 14 | edge(int to, int cap, int cost, int rev) : to(to), cap(cap), cost(cost), rev(rev) {} 15 | }; 16 | struct MCMF { 17 | int n, h[maxn], d[maxn], pre[maxn], num[maxn]; 18 | vector G[maxn]; 19 | void init(int n) { 20 | this->n = n; 21 | for (int i = 0; i <= n; ++i) 22 | G[i].clear(); 23 | } 24 | void add_edge(int from, int to, int cap, int cost) { 25 | G[from].push_back(edge(to, cap, cost, G[to].size())); 26 | G[to].push_back(edge(from, 0, -cost, G[from].size() - 1)); 27 | } 28 | //flow是自己传进去的变量,就是最后的最大流,返回的是最小费用 29 | int solve(int s, int t, int &flow, int limit = inf) { 30 | int cost = 0; 31 | memset(h, 0, sizeof(h)); 32 | while (limit) { 33 | priority_queue, vector>, greater> > Q; 34 | for (int i = 0; i <= n; ++i) 35 | d[i] = inf; 36 | d[s] = 0; 37 | Q.emplace(0, s); 38 | while (!Q.empty()) { 39 | auto now = Q.top(); Q.pop(); 40 | int u = now.second; 41 | if (d[u] < now.first) continue; 42 | for (int i = 0; i < G[u].size(); ++i) { 43 | edge &e = G[u][i]; 44 | if (e.cap > 0 && d[e.to] > d[u] + e.cost + h[u] - h[e.to]) { 45 | d[e.to] = d[u] + e.cost + h[u] - h[e.to]; 46 | pre[e.to] = u; 47 | num[e.to] = i; 48 | Q.emplace(d[e.to], e.to); 49 | } 50 | } 51 | } 52 | if (d[t] == inf) break; 53 | for (int i = 0; i <= n; ++i) 54 | h[i] += d[i]; 55 | int a = limit; 56 | for (int u = t; u != s; u = pre[u]) 57 | a = min(a, G[pre[u]][num[u]].cap); 58 | limit -= a; flow += a; cost += a * h[t]; 59 | for (int u = t; u != s; u = pre[u]) { 60 | edge &e = G[pre[u]][num[u]]; 61 | e.cap -= a; 62 | G[u][e.rev].cap += a; 63 | } 64 | } 65 | return cost; 66 | } 67 | }mcmf; 68 | int main() 69 | { 70 | freopen("D:\\in.txt", "r", stdin); 71 | int n, m, k, flow = 0; 72 | scanf("%d %d %d", &n, &m, &k); 73 | mcmf.init(n + 10); 74 | for (int i = 1; i <= m; ++i) { 75 | int x, y, c, w; 76 | scanf("%d %d %d %d", &x, &y, &c, &w); 77 | mcmf.add_edge(x, y, c, w); 78 | } 79 | auto start = clock(); 80 | printf("%d\n", mcmf.solve(1, n, flow, k)); 81 | printf("flow: %d\n", flow); 82 | double tot = static_cast(clock() - start) / CLOCKS_PER_SEC; 83 | printf("MCMF-dijkstra: %f\n", tot); 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /算法/网络流/MCMF-spfa.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | using namespace std; 10 | const int maxn = 20100; 11 | const int inf = 1 << 30; 12 | struct edge { 13 | int from, to, cap, flow, cost; 14 | edge(int u, int v, int c, int f, int w) : from(u), to(v), cap(c), flow(f), cost(w) {} 15 | }; 16 | struct MCMF { 17 | int n, m; 18 | vector edges; 19 | vector G[maxn]; 20 | int inq[maxn]; 21 | int d[maxn]; 22 | int p[maxn]; 23 | int a[maxn]; 24 | void init(int n) { 25 | this->n = n; 26 | for (int i = 0; i < n; ++i) 27 | G[i].clear(); 28 | edges.clear(); 29 | } 30 | void add_edge(int from, int to, int cap, int cost) { 31 | edges.push_back(edge(from, to, cap, 0, cost)); 32 | edges.push_back(edge(to, from, 0, 0, -cost)); 33 | m = edges.size(); 34 | G[from].push_back(m - 2); 35 | G[to].push_back(m - 1); 36 | } 37 | bool BellmanFord(int s, int t, int &flow, int &cost, int limit) { 38 | for (int i = 0; i < n; ++i) 39 | d[i] = inf; 40 | memset(inq, 0, sizeof(inq)); 41 | d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = inf; 42 | queue Q; 43 | Q.push(s); 44 | while (!Q.empty()) { 45 | int u = Q.front(); Q.pop(); 46 | inq[u] = false; 47 | for (unsigned i = 0; i < G[u].size(); ++i) { 48 | edge &e = edges[G[u][i]]; 49 | if (e.cap > e.flow && d[e.to] > d[u] + e.cost) { 50 | d[e.to] = d[u] + e.cost; 51 | p[e.to] = G[u][i]; 52 | a[e.to] = min(a[u], e.cap - e.flow); 53 | if (!inq[e.to]) { 54 | Q.push(e.to); 55 | inq[e.to] = true; 56 | } 57 | } 58 | } 59 | } 60 | if (d[t] == inf) 61 | return false; 62 | a[t] = min(a[t], limit - flow); 63 | flow += a[t]; 64 | cost += d[t] * a[t]; 65 | for (int u = t; u != s; u = edges[p[u]].from) { 66 | edges[p[u]].flow += a[t]; 67 | edges[p[u] ^ 1].flow -= a[t]; 68 | } 69 | return true; 70 | } 71 | int solve(int s, int t, int limit = inf) { 72 | int flow = 0, cost = 0; 73 | while (flow < limit && BellmanFord(s, t, flow, cost, limit)); 74 | return cost; 75 | } 76 | }mcmf; 77 | int main() 78 | { 79 | freopen("D:\\in.txt", "r", stdin); 80 | int n, m, k; 81 | scanf("%d %d %d", &n, &m, &k); 82 | mcmf.init(n + 10); 83 | for (int i = 1; i <= m; ++i) { 84 | int x, y, c, w; 85 | scanf("%d %d %d %d", &x, &y, &c, &w); 86 | mcmf.add_edge(x, y, c, w); 87 | } 88 | auto start = clock(); 89 | printf("%d\n", mcmf.solve(1, n, k)); 90 | double tot = static_cast(clock() - start) / CLOCKS_PER_SEC; 91 | printf("MCMF-spfa: %f\n", tot); 92 | return 0; 93 | } 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /算法/莫队算法/回滚莫队.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | left[i]表示第i块的左端点编号(块编号从1开始) 3 | right[i]表示第i块的右端点编号 4 | belong[i]表示下标i所在的块编号(下标从1开始) 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | using namespace std; 14 | const int maxn = 110000; 15 | void init() { //用O(1)的时间复杂度创建一个空的区间 16 | 17 | } 18 | void add_right(int index) { //将输入数组中下标index的元素添加到当前区间的右边 19 | 20 | } 21 | void add_left(int index) { //将输入数组中下标index的元素添加到当前区间的左边 22 | //该函数可能需要保存原状态,以便于之后状态的恢复 23 | } 24 | void breakpoint() { //记录下当前的状态以便于之后恢复 25 | //调用该函数之后会调用add_left修改状态,最后用resume恢复到调用breakpoint之前的状态 26 | } 27 | void resume() { //撤销所有的add_left操作,恢复到调用breakpoint之前的状态(一次性撤销所有的add_left) 28 | 29 | } 30 | int get_ans() { //用小于O(sqrt(n))的时间复杂度求出当前区间的解 31 | return 0; 32 | } 33 | namespace MoDui { 34 | struct query { //查询区间为[l, r],id表示是第几次查询 35 | int l, r, id; 36 | }; 37 | int left[maxn], right[maxn], belong[maxn], ans[maxn]; // ans可能要用long long存 38 | bool operator< (query a, query b) { 39 | return belong[a.l] != belong[b.l] ? belong[a.l] < belong[b.l] : a.r < b.r; 40 | } 41 | void work(int n, vector q) { //n指定数组大小(下标从1开始) 42 | int size = sqrt(n); 43 | int number = ceil((double)n / size); 44 | for (int i = 1; i <= number; ++i) { 45 | left[i] = size * (i - 1) + 1; 46 | right[i] = size * i; 47 | for (int j = left[i]; j <= right[i]; ++j) 48 | belong[j] = i; 49 | } 50 | right[number] = n; 51 | sort(q.begin(), q.end()); 52 | for (auto item : q) if (belong[item.l] == belong[item.r]) { 53 | init(); 54 | for (int i = item.l; i <= item.r; ++i) 55 | add_right(i); 56 | ans[item.id] = get_ans(); 57 | } 58 | for (int i = 0, k = 1; k <= number; ++k) { 59 | init(); 60 | for (int r = right[k]; i < q.size() && belong[q[i].l] == k; ++i) { 61 | int ql = q[i].l, qr = q[i].r; 62 | int l = right[k] + 1; 63 | if (belong[ql] != belong[qr]) { 64 | while (r < qr) { 65 | ++r; 66 | add_right(r); 67 | } 68 | breakpoint(); 69 | while (l > ql) { 70 | --l; 71 | add_left(l); 72 | } 73 | ans[q[i].id] = get_ans(); 74 | resume(); 75 | } 76 | } 77 | } 78 | } 79 | } 80 | int main() { 81 | return 0; 82 | } -------------------------------------------------------------------------------- /算法/莫队算法/带修改莫队.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | const int maxn = 210000; // maxn > n + pow(n, 2.0 / 3.0) 9 | struct query { //tp == 0对应查询操作:查询区间为[l, r] 10 | int tp, l, r, id, t;//tp == 1对应修改操作:将下标为l的位置修改为r 11 | }; 12 | inline void del(int index) { //删除下标为index的元素 13 | 14 | } 15 | inline void add(int index) { //添加下标为index的元素 16 | 17 | } 18 | inline void change(query& q, bool flag) { //将输入数组下标为q.l的值修改为q.r,flag表示这次修改是否位于当前区间内 19 | //flag为true则考虑这次修改对答案的影响,否则不考虑 20 | } 21 | inline void undo(query& q, bool flag) { //撤销修改操作,flag表示这次撤销是否位于当前处理的区间内 22 | 23 | } 24 | inline int get_ans() { //用小于O(sqrt(n))的时间复杂度求出当前区间的解 25 | return 0; 26 | } 27 | 28 | int belong[maxn], ans[maxn]; 29 | bool operator< (query a, query b) { 30 | if (belong[a.l] != belong[b.l]) 31 | return belong[a.l] < belong[b.l]; 32 | else if (belong[a.r] != belong[b.r]) 33 | return belong[a.r] < belong[b.r]; 34 | else 35 | return a.t < b.t; 36 | } 37 | void work(int n, vector q) { //n指定数组大小(下标从1开始) 38 | int size = pow(n, 2.0 / 3.0); 39 | int number = ceil((double)n / size); 40 | for (int i = 1; i <= number; ++i) 41 | for (int j = (i - 1) * size + 1; j <= i * size; ++j) 42 | belong[j] = i; 43 | vector a, b; //a记录查询,b记录修改 44 | for (auto item : q) { 45 | if (item.tp == 0) { 46 | item.id = a.size(); 47 | item.t = b.size(); 48 | a.push_back(item); 49 | } 50 | else { 51 | b.push_back(item); 52 | } 53 | } 54 | sort(a.begin(), a.end()); 55 | int l = 1, r = 0, t = 0; 56 | for (auto item : a) { 57 | while (l < item.l) del(l++); 58 | while (l > item.l) add(--l); 59 | while (r < item.r) add(++r); 60 | while (r > item.r) del(r--); 61 | while (t < item.t) { 62 | change(b[t], item.l <= b[t].l && item.r >= b[t].l); 63 | ++t; 64 | } 65 | while (t > item.t) { 66 | --t; 67 | undo(b[t], item.l <= b[t].l && item.r >= b[t].l); 68 | } 69 | ans[item.id] = get_ans(); 70 | } 71 | /* 72 | for (int i = 0; i < a.size(); ++i) 73 | printf("%d\n", ans[i]); 74 | */ 75 | } 76 | 77 | int main() { 78 | return 0; 79 | } -------------------------------------------------------------------------------- /算法/莫队算法/莫队算法.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | const int maxn = 110000; // maxn > n + sqrt(n) 8 | inline void del(int index) { //删除下标为index的元素 9 | 10 | } 11 | inline void add(int index) { //添加下标为index的元素 12 | 13 | } 14 | inline int get_ans() { //用小于O(sqrt(n))的时间复杂度求出当前区间的解 15 | return 0; 16 | } 17 | namespace MoDui { 18 | struct query { //查询区间为[l, r],id表示是第几次查询 19 | int l, r, id; 20 | }; 21 | int belong[maxn], ans[maxn]; 22 | bool operator<(query a, query b) { 23 | if (belong[a.l] != belong[b.l]) 24 | return belong[a.l] < belong[b.l]; 25 | else if (belong[a.l] & 1) 26 | return a.r < b.r; 27 | else 28 | return a.r > b.r; 29 | } 30 | void work(int n, vector q) { //n指定数组大小(下标从1开始) 31 | int size = sqrt(n); 32 | int number = ceil((double)n / size); 33 | for (int i = 1; i <= number; ++i) 34 | for (int j = (i - 1) * size + 1; j <= i * size; ++j) 35 | belong[j] = i; 36 | sort(q.begin(), q.end()); 37 | int l = 1, r = 0; 38 | for (auto item : q) { 39 | while (l < item.l) del(l++); 40 | while (l > item.l) add(--l); 41 | while (r < item.r) add(++r); 42 | while (r > item.r) del(r--); 43 | ans[item.id] = get_ans(); 44 | } 45 | } 46 | } 47 | int main() { 48 | return 0; 49 | } -------------------------------------------------------------------------------- /算法/高精度/分数类.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | using integer = long long; 9 | struct frac { 10 | integer num, den; 11 | frac() : num(0), den(1){} 12 | frac(integer val) { 13 | num = val; 14 | den = 1; 15 | } 16 | frac(integer a, integer b) { 17 | integer g = gcd(a, b); 18 | num = a / g; 19 | den = b / g; 20 | if (den < 0) { 21 | num = -num; 22 | den = -den; 23 | } 24 | } 25 | }; 26 | frac operator+ (frac x, frac y) { 27 | return frac(x.num * y.den + y.num * x.den, x.den * y.den); 28 | } 29 | frac operator- (frac x, frac y) { 30 | return frac(x.num * y.den - y.num * x.den, x.den * y.den); 31 | } 32 | frac operator* (frac x, frac y) { 33 | return frac(x.num * y.num, x.den * y.den); 34 | } 35 | frac operator/ (frac x, frac y) { 36 | return frac(x.num * y.den, x.den * y.num); 37 | } 38 | frac operator+ (frac x, integer val) { 39 | return frac(x.num + x.den * val, x.den); 40 | } 41 | frac operator* (frac x, integer val) { 42 | return frac(x.num * val, x.den); 43 | } 44 | frac operator/ (frac x, integer val) { 45 | return frac(x.num, x.den * val); 46 | } 47 | bool operator== (frac x, frac y) { 48 | return x.num == y.num && x.den == y.den; 49 | } 50 | bool operator!= (frac x, frac y) { 51 | return x.num != y.num || x.den != y.den; 52 | } 53 | bool operator< (frac x, frac y) { 54 | return x.num * y.den < y.num * x.den; 55 | } 56 | frac abs(frac x) { 57 | if (x.num < 0) 58 | return frac(-x.num, x.den); 59 | return x; 60 | } 61 | int main() { 62 | printf("%d\n", gcd(0, 6)); 63 | return 0; 64 | } -------------------------------------------------------------------------------- /算法/高精度/大整数类.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunkafei/xcpc-algorithm-templates/19e3ff342c0b2a221d81aeab88038a52e0e010d4/算法/高精度/大整数类.cpp -------------------------------------------------------------------------------- /算法/高精度/高精度定点数.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | class fixed { // 高精度定点数 5 | private: 6 | static constexpr int num_blocks = 40; 7 | static constexpr int offset = (num_blocks / 2) * 64; 8 | std::array data = {}; 9 | public: 10 | fixed(double value) { 11 | uint64_t bits = *(uint64_t*)&value; 12 | uint64_t fraction = bits & 0xfffffffffffff; 13 | uint64_t exponent = (bits & 0x7ff0000000000000) >> 52; 14 | uint64_t sign = bits >> 63; 15 | if (exponent != 0) { 16 | exponent -= 1023; 17 | fraction |= (1ull << 52); 18 | } 19 | else { 20 | exponent = -1022; 21 | } 22 | exponent += offset - 52; 23 | data[exponent / 64] |= (fraction << (exponent % 64)); 24 | fraction <<= 11; 25 | data[(exponent + 52) / 64] |= (fraction >> (63 - (exponent + 52) % 64)); 26 | if (sign) { 27 | for (int i = 0; i < num_blocks; ++i) { 28 | data[i] = ~data[i]; 29 | } 30 | for (int i = 0; i < num_blocks; ++i) { 31 | data[i] += 1; 32 | if (data[i]) { 33 | break; 34 | } 35 | } 36 | } 37 | } 38 | explicit operator double() const { 39 | decltype(this->data) data = this->data; 40 | uint64_t sign = 0; 41 | if (data[num_blocks - 1] & (1ull << 63)) { 42 | sign = 1; 43 | for (int i = 0; i < num_blocks; ++i) { 44 | data[i] = ~data[i]; 45 | } 46 | for (int i = 0; i < num_blocks; ++i) { 47 | data[i] += 1; 48 | if (data[i]) { 49 | break; 50 | } 51 | } 52 | } 53 | int64_t top = -1; 54 | for (int i = num_blocks - 1; i >= 0; --i) { 55 | if (data[i]) { 56 | for (int j = 63; j >= 0; --j) { 57 | if (data[i] & (1ull << j)) { 58 | top = i * 64 + j; 59 | goto finish; 60 | } 61 | } 62 | } 63 | } 64 | if (top == -1) { 65 | return 0; 66 | } 67 | finish:; 68 | int64_t exponent = top - offset + 1023; 69 | uint64_t fraction = 0; 70 | if (exponent >= 2047) { 71 | exponent = 2047; 72 | } 73 | else { 74 | for (int i = 1; i <= 52; ++i) { 75 | auto block = (top - i) / 64; 76 | auto num = (top - i) % 64; 77 | if (data[block] & (1ull << num)) { 78 | fraction |= (1ull << (52 - i)); 79 | } 80 | } 81 | uint64_t carry = 0; 82 | if (data[(top - 53) / 64] & (1ull << ((top - 53) % 64))) { 83 | for (int i = 54; i <= 54 + 64; ++i) { 84 | auto block = (top - i) / 64; 85 | auto num = (top - i) % 64; 86 | if (data[block] & (1ull << num)) { 87 | carry = 1; 88 | break; 89 | } 90 | } 91 | if (!carry) { 92 | auto block = (top - 53) / 64; 93 | for (int i = 0; i < block; ++i) if (data[i]) { 94 | carry = 1; 95 | break; 96 | } 97 | } 98 | if (!carry) { 99 | if (fraction & 1) { 100 | carry = 1; 101 | } 102 | } 103 | } 104 | fraction += carry; 105 | if (fraction > 0xfffffffffffff) { 106 | fraction = 0; 107 | exponent += 1; 108 | } 109 | if (exponent <= 0) { 110 | fraction |= (1ull << 52); 111 | fraction >>= -exponent + 1; 112 | exponent = 0; 113 | } 114 | } 115 | fraction &= 0xfffffffffffff; 116 | uint64_t ret = (sign << 63) | (exponent << 52) | fraction; 117 | return *(double*)&ret; 118 | } 119 | void operator+= (const fixed& rhs) { 120 | uint64_t overflow = 0; 121 | for (int i = 0; i < num_blocks; ++i) { 122 | uint64_t value = rhs.data[i] + overflow; 123 | if (value < overflow) { 124 | overflow = 1; 125 | } 126 | else { 127 | overflow = 0; 128 | } 129 | data[i] += value; 130 | if (data[i] < value) { 131 | overflow = 1; 132 | } 133 | } 134 | } 135 | fixed operator+ (const fixed& rhs) const { 136 | fixed ret = *this; 137 | ret += rhs; 138 | return ret; 139 | } 140 | void debug() const { 141 | for (int i = 0; i < num_blocks; ++i) { 142 | for (int j = 0; j < 64; ++j) { 143 | if (data[i] & (1ull << j)) { 144 | printf("bit: %d\n", i * 64 + j - offset); 145 | } 146 | } 147 | } 148 | } 149 | }; 150 | int main() { 151 | return 0; 152 | } --------------------------------------------------------------------------------