1. 算法竞赛与模板的价值
参加过算法竞赛的同学都知道,比赛中时间就是生命。当你在赛场上遇到一道经典题型时,如果还要从头推导解法,那基本就与奖牌无缘了。这就是为什么我们需要准备算法模板——它们就像是战士的武器库,关键时刻能帮你节省大量时间。
我在蓝桥杯、ACM等赛事中摸爬滚打多年,发现80%的题目都能用20%的经典算法解决。把这些高频算法整理成随时可调用的模板,比赛时就能把精力集中在问题分析和调试上,而不是反复重写基础代码。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心模板分类与解析
2.1 基础数据结构模板
2.1.1 并查集(Disjoint Set Union)
并查集是处理不相交集合的高效数据结构,在连通性问题中表现优异。标准模板需要包含路径压缩和按秩合并两个优化:
cpp复制class DSU {
private:
vector<int> parent, rank;
public:
DSU(int n) : parent(n), rank(n, 0) {
iota(parent.begin(), parent.end(), 0);
}
int find(int x) {
return parent[x] == x ? x : parent[x] = find(parent[x]);
}
void unite(int x, int y) {
x = find(x), y = find(y);
if(x == y) return;
if(rank[x] < rank[y]) swap(x, y);
parent[y] = x;
if(rank[x] == rank[y]) rank[x]++;
}
};
注意事项:按秩合并能保证树高为O(α(n)),实际使用时可以简化掉rank数组,仅保留路径压缩也能获得不错的效率。
2.1.2 线段树(Segment Tree)
线段树是处理区间查询的利器,以下是支持区间求和与区间更新的模板:
cpp复制class SegmentTree {
private:
vector<int> tree, lazy;
int n;
void push_down(int node, int l, int r) {
if(lazy[node] == 0) return;
int mid = (l + r) / 2;
tree[node*2] += lazy[node] * (mid - l + 1);
tree[node*2+1] += lazy[node] * (r - mid);
lazy[node*2] += lazy[node];
lazy[node*2+1] += lazy[node];
lazy[node] = 0;
}
public:
SegmentTree(const vector<int>& nums) {
n = nums.size();
tree.resize(4 * n);
lazy.resize(4 * n, 0);
build(1, 0, n-1, nums);
}
void build(int node, int l, int r, const vector<int>& nums) {
if(l == r) {
tree[node] = nums[l];
return;
}
int mid = (l + r
