算法模板
个人使用的算法模板
nreyog
这个作者很懒,什么都没留下…
展开
-
树链剖分
例题:「一本通 4.5 例 1」树的统计 // from:https://oi-wiki.org/graph/hld/ #include <algorithm> #include <cstdio> #include <cstring> #define lc o << 1 #define rc o << 1 | 1 const int maxn = 60010; const int inf = 2e9; int原创 2022-04-11 22:45:04 · 335 阅读 · 0 评论 -
排序算法
快速排序 选定第一个元素x为中心轴 将小于x的数字放在x的左边 将大于x的数字放在x的右边 分别对左右子数组重复前3步操作 代码: #include <bits/stdc++.h> using namespace std; void quick_sort(vector<int>& a, int l, int r) { if (l >= r) return; int mid = a[l], left = l, right = r; wh原创 2022-03-06 16:06:58 · 452 阅读 · 0 评论 -
网络流
想看定义和证明可以到算法学习笔记(28): 网络流 - 知乎 最大流 EK算法 1.残留网络的可行流+原图的可行流=原题的另一个可行流 2.可行流的残留网络中不存在增广路,那么就找到了最大流 // 例题https://www.acwing.com/problem/content/description/2173/ // EK算法时间复杂度为O(点数*边数^2),但是这个复杂度非常玄学,上界非常宽松 // 基本可以解决点数+边数<10000的问题 #include <bits/s.原创 2022-02-25 21:17:24 · 376 阅读 · 0 评论 -
最小生成树
用n - 1条边将n个点连起来,当n - 1条边的权值之和最小,形成的树就是最小生成树 如这张图: 的最小生成树为 4条边将5个点连起来,权值之和为7 问题:给你一个n个点的带权无向连通图,节点编号为0到n - 1,同时还有一个数组edges,其中edges[i] = [from,to,weight] 表示在from和to节点之间有一条权值为weight的无向边,求这个图的最小生成树的权值和 下面用kruskal算法解决该问题: 步骤: 1、cnt记录当前生成树中的边数,res记录权值之和..原创 2022-02-15 19:53:53 · 175 阅读 · 0 评论 -
匈牙利算法
先来看这么一个问题:一个班级里有n个男生和m个女生,我们有一个n × m的矩阵,grid[i][j] = 1表示第i个男生和第j个女生相互喜欢,问这个班最多能凑出几对情侣(情侣定义:一男一女) 标准的二分图最大匹配问题,只可意会不可言传,具体看代码 class Solution: def maximumInvitations(self, grid: List[List[int]]) -> int: n = len(grid) m = len(grid[.原创 2022-02-14 20:37:54 · 252 阅读 · 0 评论 -
树的直径
leetcode1245. 树的直径 给你这棵「无向树」,请你测算并返回它的「直径」:这棵树上最长简单路径的边数。 我们用一个由所有「边」组成的数组 edges来表示一棵无向树,其中edges[i] = [u, v]表示节点u 和 v之间的双向边。 树上的节点都已经用{0, 1, ..., edges.length}中的数做了标记,每个节点上的标记都是独一无二的。 0 <= edges.length <10^4 edges[i][0] != edges[i][1] ...原创 2022-02-08 15:32:43 · 83 阅读 · 0 评论 -
ST表
背景:给定n个数,有m个询问,对于每个询问,你需要回答区间[l, r]中的最大值 , ST表基于倍增的思想,可用做到O(nlogn)预处理,O(1)回答每个询问,但是不支持修改操作 ST表板子题 #include <bits/stdc++.h> using namespace std; int a[100005], st[100005][20], n, m; void init() { int k = __lg(n); for(int i = 1; i <..原创 2022-02-05 17:49:43 · 399 阅读 · 0 评论 -
DFS序
DFS序:每个节点在DFS深度优先遍历中的进出栈的时间序列 树是一种非线性结构,所以对树进行区间操作会很不方便,所以要考虑将树转化成数组形式以便操作 对树进行一趟DFS,同时维护一个时间戳,记录每个结点被访问的时间,可以想象,对同一棵子树上的结点,访问的时间一定是相连的,映射到数组上就是一片连续的区间,如此,对一棵子树进行操作,可以等价于对一片连续的区间进行操作,区间操作常常可以使用树状数组或线段树维护 我们常常需要维护以下信息: Time:时间戳 l[i]:dfs进入子树i的时间 r[i]:.原创 2021-12-03 13:06:51 · 963 阅读 · 1 评论 -
求组合数
方法一: 由组合数公式进行递推 其实就是求出杨辉三角 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 1 6 15 20 15 6 1 ... 这个两重循环就能求,当枚举的是每一行的最左边元素,就c[i][0] = 1 否则c[i][j] = c[i - 1][j] + c[i - 1][j - 1] #include <bits/stdc++.h> using namespace std; int c[5005][5005], ...原创 2021-11-27 00:32:18 · 259 阅读 · 0 评论 -
分块算法
通常把长度为n的数列分成块 在进行区间操作前通常需要预处理以下数据: 数组l,l[i]表示第i块的最左边元素的位置 数组r,r[i]表示第i块的最右边元素的位置 数组pos,pos[i]表示第i个元素在第几个块内 注:若是有多余元素,则多余元素算入最后一个块内,如:有10个元素,则块数为下取整,也就是3个块,此时分块情况为{1,2,3},{4,5,6},{7,8,9,10} 分块算法的精髓在于,对于一次区间操作,对区间內部的整块进行整体的操作,对区间边缘的零散块单独暴力处理 在对区间[left.原创 2021-11-09 19:35:33 · 948 阅读 · 0 评论 -
线性基
可以解决与异或有关的题目 思路:在一个较大的集合中(假设有1e5个数),有一些数是没用意义的,因为它可以由其它数异或得到,我们若是能消除这些没有意义的数,得到一个较小的集合(60个数左右),使得对这个较小的集合进行操作,等价于对大集合进行操作,显然可以极大的提升时间复杂度。 对于任意一个集合,如果其中有两个元素的最高位都是1,可以用异或将其中一个元素替换掉,如此往复,可以让集合用某一位作为最高位的数唯一,这样我们就可以用60个数的集合,来表示1e5个数的集合。 被替换掉的数显然可以通过集合元素异或得.原创 2021-10-27 21:42:59 · 3736 阅读 · 0 评论 -
KMP算法
一种十分优秀的字符串匹配算法,时间复杂度为线性 首先定义如下变量: a - 文本串 b - 模式串 现求b串在a串中出现的次数 我们又定义如下变量: i - 指向文本串a的指针 j - 指向文本串b的指针 nxt - nxt[j]表示串b[0:j]中既是前缀也是后缀(不包括b[0:j]自身)的字符串的最大长度 res - 记录串b在串a中出现的次数 在a[i] != b[j]时,当前位置发生匹配失败,此时我们应该想到回退j,使得b[0: j]这样的最长前缀能够匹配到a[i - j..原创 2021-10-13 19:12:06 · 87 阅读 · 0 评论 -
快读、快写
只能读整型 #include <bits/stdc++.h> using namespace std; template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <=原创 2021-10-08 20:23:28 · 147 阅读 · 0 评论 -
可持久化线段树
可持久化线段树的意思就是可以查询和修改历史版本线段树 具体实现: 我们采用动态开点的方式来建立线段树 build和query操作与普通动态开点线段树如出一辙 对于修改操作,我们新增修改后的结点及其父结点,未修改过的结点直接copy过来 对于一次修改,就是一次新增祖宗的过程,也是一次添加新版本的过程 各变量含义: t[i]- i所表示区间的权值 lson[p] - p结点左儿子所在位置 rson[p] - p结点右儿子所在位置 v[i] - 版本i起始结点所在位置 cnt - ...原创 2021-10-07 16:50:38 · 234 阅读 · 0 评论 -
质数的筛法
埃氏筛:用质数把质数的倍数筛掉 #include <bits/stdc++.h> using namespace std; bool prime[1000005]; int main() { int n; cin >> n; memset(prime, 1, sizeof prime); for (int i = 2; i <= n; i++) { if (prime[i]) { for (int j原创 2021-10-05 13:10:43 · 144 阅读 · 0 评论 -
高斯消元解方程
题目链接:P3389 高斯消元法 算法原理: 两方程互换,解不变 一方程乘以非零数k,解不变 一方程乘以数k加上另一方程,解不变 代码: #include <bits/stdc++.h> using namespace std; double a[105][105]; int main() { int n; cin >> n; for (int i = 0; i < n; i++) { for (int j = 0;..原创 2021-10-04 14:07:55 · 183 阅读 · 0 评论 -
最短路算法
dijkstra:119.单源最短路 #include <bits/stdc++.h> using namespace std; typedef long long ll; ll n, m, s, t; ll g[3000][3000]; bool r[3000]; int main() { cin >> n >> m >> s >> t; for (int i = 0; i <= n; i++) { fo原创 2021-10-01 18:15:02 · 148 阅读 · 0 评论 -
关于除法取模
(a / b) % mod = a * pow(b, mod - 2, mod) % mod >>> mod = 10 ** 9 + 7 >>> a = 20000000000 >>> b = 2 >>> (a // b) % mod 999999937 >>> a * pow(b, mod - 2, mod) % mod 999999937 将除法操作转化为乘法可以避免误差 ...原创 2021-09-29 12:38:54 · 153 阅读 · 0 评论 -
动态开点线段树
适用场景: 需要开一棵空间非常大的线段树 数组下标需要以负数为索引 具体实现: 我们是一边修改一边动态建立线段树,所以不需要build 获取左右儿子不再是p * 2 和 p * 2 + 1,而是用两个数组 lson[p] 和 rson[p] 存两个儿子所在位置 与lazy标记有同样的思想,需要访问儿子时,再动态地为它分配位置,所以大多数儿子都是以虚拟的方式存在,这样就避免了空间浪费 优点: 解决了空间限制的问题 无视索引下标的限制,用一个数组元素表示一段区间的值。 例如,求数组下标 -100.原创 2021-09-28 20:20:28 · 386 阅读 · 0 评论 -
二进制枚举
C++: #include <bits/stdc++.h> using namespace std; int main() { int n; cin >> n; for (int i = 0; i < (1 << n); i++) { cout << i << "的子集有:"; for (int j = i; j; j = (j - 1) & i) {原创 2021-09-21 18:21:33 · 163 阅读 · 0 评论 -
树状数组
单点修改,区间查询 int t[1000005], n, m; void add(int x, int v) { for (; x <= n; x += x & -x) { t[x] += v; } } int ask(int x) { int res = 0; for (; x > 0; x -= x & -x) { res += t[x]; } return res; } 区间修改,区间查询原创 2021-09-19 16:11:30 · 188 阅读 · 0 评论 -
字符串哈希
1、字符串匹配:实现 strStr() (当KMP用,不会KMP的菜鸡留下了泪水) typedef unsigned long long ull; ull p[100000], pre[100000]; class Solution { public: ull get(int l, int r) { return pre[r] - pre[l - 1] * p[r - l + 1]; } int strStr(string s, string needle)..原创 2021-09-17 13:31:00 · 96 阅读 · 0 评论 -
快速幂
C++: #include <bits/stdc++.h> using namespace std; const int MOD = 1e9 + 7; int pow(int a, int b) { int res = 1; while (b) { if (b & 1) { res = (long long)res * a % MOD; } a = (long long)a * a % MOD;原创 2021-09-17 13:20:54 · 111 阅读 · 0 评论 -
模拟退火随机大法
如:求最小权值 各变量含义 T:当前温度 T_min:熄火时刻 r:退火速度 delta:距离差 now:当前状态 next:随机选的状态 calc(now):当前状态权值 res:当前最优解 cnt:执行模拟退火次数 import random import math n = int(input()) now = list(range(0, n)) # 初始化状态 res = float('inf') cnt = 50 def calc(st): res = 0原创 2021-09-16 23:03:30 · 138 阅读 · 0 评论 -
前缀树
题目链接:实现Trie(前缀树) 请你实现 Trie 类: Trie() 初始化前缀树对象。 void insert(String word) 向前缀树中插入字符串 word 。 boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。 boolean startsWith(String prefix) 如果之前已经插入的字符串word 的前缀之一为 prefix ,返回 true ;否则,返回 fa.原创 2021-09-16 22:55:42 · 157 阅读 · 0 评论 -
线段树(区间修改,区间查询)
题目链接:区间修改,区间查询 build,基于后序遍历和区间二分的思想,递归建树 change,1、r<s||l>t,当前区间与待修改区间没有交集,直接return 2、s<=l&&r<=t,当前区间为待修改区间的子区间,更新区间权值和懒标记 3、当前区间与待修改区间有部分交集,则将懒标记下传,并递归查找子区间,同时维护线段树 query:1、r<s||l>t,当前区间与查询区间没有交集,返回0 2、s&l...原创 2021-09-16 18:09:26 · 678 阅读 · 0 评论 -
线段树(单点修改,区间查询)
题目链接:单点修改,区间查询 build:基于后序遍历和区间二分的思想,递归建树 change:1、r<x||l>x,当前区间与要修改的点无交集,直接return掉 2、l==r&&l==x,这个点为要修改的点,把对应位置修改后return 3、其他情况,递归找要修改的点,同时维护修改后的树 query:1、r<s||l>t,当前区间与查询区间无交集,返回0 2、s<=l&&r<=...原创 2021-09-16 17:45:28 · 323 阅读 · 0 评论