- B1: 前缀和与差分
- B2: 快速组合
- B3: 二分查找
- B4: 高精度加法
- B4: 高精度除法
- B4: 高精度乘法
- B4: 高精度减法
- D1: 单调栈
- D2: 单调队列
- D3: Spare Table
- D4: 字典树
- D5: 并查集
- D6: 树状数组-限定区间计数
- D6: 树状数组-单点更新区间求和
- D7: 线段树-基础
- G1: 二分图-判定
- G2: 二分图-最大匹配
- G3: Astar k短路 on matrix
- G4: 拓扑排序
- G5: 连通无环无向图的重心
- G6: MST-Kruskal
- G7: MST-Prim
- G8: ShortestPath-BellmanFord
- G9: ShortestPath-Dijkstra
- G9: ShortestPath-Dijkstra Heap OPT
- G10: ShortestPath-SPFA
- G11: ShortestPath-Floyd
- M1: 快速幂与龟速乘
- M2: 欧几里得-最大公约数
- M2: EX欧几里得-翡蜀定理
- M2: EX欧几里得-线性同余方程
- M2: EX欧几里得-乘法逆元
- M3: 费马小定理-乘法逆元
- M4: 分解质因数
- M4: 分解质因数-欧拉函数
- M4: 分解质因数-多数乘积约数计数
- M4: 分解质因数-多数乘积约数求和
- M5: 欧拉筛
- M5: 欧拉筛-质数筛
- M6: 埃氏筛-质数筛
- M7: Stein算法-最大公约数
- M8: 矩阵乘法与快速幂
- O1: 快速排序
- O2: 并归排序
- S1: KMP
- S2: 字符串哈希
This doc has been marked needs-review. Following information are in upcoming update plan.
template< class type >
class SPFA {
const int n;
bool vis[maxn+1] {}; // 表示该点(被松弛成功但还没有松弛其出边)的状态, 也即是否在队列中
type cnt[maxn+1] {}; // 表示源点到该点的最短路径的边数
queue<int> que;
public:
struct node {
int to, ptr;
type len;
} ar[maxm+1]; // 链式前向星存图, ar[0]不存, ptr==0视为ptr==null
int head[maxn+1] {};
type INF = numeric_limits<type>::max()>>1u;
type dis[maxn+1] {}; // 表示源点到该点的最短路径长度
explicit SPFA(const int n): n(n) {}
bool solve(const int source) {
fill(cnt+1, cnt+n+1, 0); // 如不连通,必须全为0,连通的话只需源点为0
fill(dis+1, dis+n+1, INF); // 判断负环,只需保证算法过程不会溢出即可(都为相同的数,减少运算),也统一这么做吧,INF这个值比较好
fill(vis+1, vis+n+1, true); // 为了判断负环, 因为有图并不全连通的可能所以需要vis全部标记并放入队列
queue<int>().swap(que);
for (int i = 1; i <= n; i++) que.push(i); // 如果题目保证全连通, 可以只操作源点
dis[source] = 0; // 确定源点到源点的最短路,没有更新,所以在vis中标记源点。如只需判断负环,则这一步不需要
while (!que.empty()) {
static int cur;
vis[cur = que.front()] = false, que.pop();
for (int i = head[cur]; i; i = ar[i].ptr) { // 尝试松弛其出边
static type dist;
if ((dist = dis[cur] + ar[i].len) < dis[ar[i].to]) {
dis[ar[i].to] = dist;
if ((cnt[ar[i].to] = cnt[cur] + 1) == n) return false; // 抽屉原理
if (!vis[ar[i].to]) que.push(ar[i].to), vis[ar[i].to] = true;
}
}
}
return true;
}
};
单源汇
template< class type >
class SP {
type dis[maxn + 1] {};
bool vis[maxn + 1] {};
const int n;
queue< int > que;
public:
const int INF = numeric_limits< type >::max() >> 1u;
struct {
int to, ptr;
type len;
} ar[maxm + 1] {};
int head[maxn + 1] {};
explicit SP(const int n): n(n) {}
type solve(int source, int target) {
fill(vis + 1, vis + n + 1, false);
fill(dis + 1, dis + n + 1, INF);
queue< int >().swap(que);
vis[source] = true, dis[source] = 0, que.push(source);
while (!que.empty()) {
static int cur;
cur = que.front(), que.pop(), vis[cur] = false;
for (int i = head[cur]; i; i = ar[i].ptr)
if (dis[cur] + ar[i].len < dis[ar[i].to]) {
dis[ar[i].to] = dis[cur] + ar[i].len;
if (!vis[ar[i].to]) {
que.push(ar[i].to);
vis[ar[i].to] = true;
}
}
}
return dis[target];
}
};
拓展应用
判断负环
template< class type >
class SP {
const type INF = numeric_limits< type >::max() >> 1u;
type dis[maxn + 1] {};
bool vis[maxn + 1] {};
int cnt[maxn + 1] {};
const int n;
queue< int > que;
public:
struct {
int to, ptr;
type len;
} ar[maxm + 1] {};
int head[maxn + 1] {};
explicit SP(const int n): n(n) {}
bool solve() {
fill(vis + 1, vis + n + 1, true);
fill(cnt + 1, cnt + n + 1, 0);
fill(dis + 1, dis + n + 1, INF);
queue< int >().swap(que);
for (int i = 1; i <= n; i++) que.push(i);
while (!que.empty()) {
static int cur;
cur = que.front(), que.pop(), vis[cur] = false;
for (int i = head[cur]; i; i = ar[i].ptr)
if (dis[cur] + ar[i].len < dis[ar[i].to]) {
if ((cnt[ar[i].to] = cnt[cur] + 1) == n) return false;
dis[ar[i].to] = dis[cur] + ar[i].len;
if (!vis[ar[i].to]) {
que.push(ar[i].to);
vis[ar[i].to] = true;
}
}
}
return true;
}
};