一、同余最短路
同余最短路是处理线性组合表示数或最小代价满足同余条件问题的利器。核心思想:用最短路算法维护不同同余类的最小代价,将数论问题转化为图上最短路问题。
(一)核心场景
- 给定数 \(a_1, a_2, \dots, a_n\),判断某数 \(x\) 能否用其非负系数线性组合表示(如 Gym100753M )。
- 寻找 \(n\) 的倍数中,各位数字之和最小的数(如 arc084D )。
(二)解题思路
- 取最小的 \(a_i\) 作为模数 \(m\),将数按模 \(m\) 分类。
- 对同余类 \(r \in [0, m-1]\),维护到达该类的最小代价(如数字和、数值大小 )。
- 建图:从 \(r\) 到 \((r + a_i) \mod m\) 连边,边权为 \(a_i\) 的代价,用 BFS 或 Dijkstra 求最短路。
(三)示例代码(arc084D 思路)
int n;
cin >> n;
vector<int> dist(n, INT_MAX);
queue<int> q;
dist[0] = 0;
q.push(0);while (!q.empty()) {int u = q.front();q.pop();for (int d = 0; d <= 9; ++d) {int v = (u * 10 + d) % n;int new_sum = dist[u] + d;if (new_sum < dist[v]) {dist[v] = new_sum;q.push(v);}}
}
cout << dist[0] << endl;
二、差分约束系统
差分约束系统用于解决多元一次不等式组的可行性问题,或在可行解中求某变量最值。核心是将不等式转化为图中边的关系,通过最短路/最长路求解。
(一)核心转化
- 不等式 \(x_i - x_j \leq c\) 等价于 \(x_i \leq x_j + c\),对应图中边 \(j \to i\),权值为 \(c\)(求最短路 )。
- 不等式 \(x_i - x_j \geq c\) 等价于 \(x_j \leq x_i - c\),对应图中边 \(i \to j\),权值为 \(-c\)(求最短路 )。
- 若存在负环,则无解;否则,最短路数组即为一组可行解。
(二)典型例题
- P1993:基础差分约束问题,直接转化不等式建图,用 SPFA 判断负环。
- acmsguru-298:增加 \(|A_i| \leq 10000\) 约束,需补充 \(A_i \leq 10000\) 和 \(A_i \geq -10000\) 的边,目标求 \(A[n] - A[1]\) 的最小值(即 \(A[n] \leq A[1] + k\) 中的最小 \(k\) )。
(三)示例代码(SPFA 判断可行性)
int n, m;
cin >> n >> m;
vector<vector<pair<int, int> > > adj(n + 1);
for (int i = 0; i < m; ++i) {int j, i, c;cin >> j >> i >> c;adj[j].emplace_back(i, c);
}vector<int> dist(n + 1, INT_MAX);
vector<int> cnt(n + 1, 0);
vector<bool> inque(n + 1, false);
dist[1] = 0;
queue<int> q;
q.push(1);
inque[1] = true;bool has_neg_cycle = false;
while (!q.empty()) {int u = q.front();q.pop();inque[u] = false;for (auto [v, w] : adj[u]) {if (dist[v] > dist[u] + w) {dist[v] = dist[u] + w;if (!inque[v]) {q.push(v);inque[v] = true;cnt[v]++;if (cnt[v] >= n) {has_neg_cycle = true;break;}}}}if (has_neg_cycle) break;
}if (has_neg_cycle) cout << "No solution" << endl;
else cout << "Feasible" << endl;
三、2-SAT
2-SAT 用于解决布尔变量的约束满足问题(变量仅取 0 或 1,约束为变量间逻辑关系,如与、或、非、异或等 )。核心是将逻辑约束转化为蕴含关系,通过强连通分量(SCC)判断可行性。
(一)核心转化
- 变量 \(x\) 对应两个节点:\(x\) 表示 \(x=1\),\(\neg x\) 表示 \(x=0\)。
- 约束 \(a \lor b\) 等价于 \(\neg a \to b\) 且 \(\neg b \to a\),对应图中两条边。
- 若变量 \(x\) 的 \(x\) 与 \(\neg x\) 在同一 SCC,则无解;否则,取 SCC 编号大的节点作为变量值。
(二)典型例题
- P4782:2-SAT 模板题,直接按约束建图,用 Tarjan 求 SCC 判断。
- CF776D:遥控器控制门状态翻转,将门状态约束转化为 2-SAT 条件。
- CF1971H:3 行 n 列矩阵每列至少两个 1,将位置符号作变量,约束转化为“至少两个为 1”的蕴含关系。
四、LCA(最近公共祖先)
LCA 是树上两节点最近的公共祖先,是树论基础问题,有多种高效求解方法。
(一)常用方法
- 倍增法:预处理节点的 \(2^k\) 祖先,查询时二进制拆分跳转,时间复杂度 \(O(n\log n + q\log n)\)。
- 树链剖分:拆树为链,通过链跳转求 LCA,复杂度同倍增,常数略大。
- Tarjan 离线算法:用并查集一次性处理查询,复杂度 \(O(n + q)\),需离线。
- ST 表:基于欧拉序的 RMQ(区间最值查询),预处理 \(O(n\log n)\),查询 \(O(1)\)。
五、欧拉回路
欧拉回路是指从某点出发,遍历每条边恰好一次并回到起点的路径。判断和构造欧拉回路是图论中的经典问题。
(一)存在条件
- 无向图:所有顶点度数为偶数,且图连通(孤立点除外)。
- 有向图:所有顶点入度等于出度,且图连通(孤立点除外)。
(二)典型例题
- uoj 117:模板题,判断并构造欧拉回路。
- CF209C:求最少添加多少条边使无向图存在欧拉回路,需同时处理连通性(每个连通分量至少一个点)和奇偶性(度数为奇的点对数除以 2)。
六、三/四元环计数
环计数是图论中较难的问题,三元环(3 个节点构成的环)和四元环(4 个节点构成的环)是常见类型,需高效枚举避免超时。
(一)三元环计数
核心思路:按度数分桶,对度数小的节点枚举其邻接点,检查邻接点间是否有边,复杂度 \(O(m\sqrt{m})\)。
