NOI大纲复健计划
2.2.3 数据结构
1. 线性结构
【 5 】双端栈
【 5 】双端队列
【 5 】单调队列
【 6 】优先队列
【 6 】ST 表(Sparse Table)
2. 集合与森林
【 6 】并查集
【 6 】树的孩子兄弟表示法
3. 特殊树
【 6 】二叉堆
【 6 】树状数组
【 6 】线段树
【 6 】字典树(Trie树)
【 7 】笛卡尔树
【 8 】平衡树:AVL、treap、splay等
4. 常见图
【 5 】稀疏图
【 6 】偶图(二分图)
【 6 】欧拉图
【 6 】有向无环图
【 7 】连通图与强连通图
【 7 】双连通图
5. 哈希表
【 5 】数值哈希函数构造
【 6 】字符串哈希函数构造
【 6 】哈希冲突的常用处理方法
2.2.4 算法
1. 复杂度分析
【 6 】时间复杂度分析
【 6 】空间复杂度分析
2. 算法策略
【 6 】离散化
3. 基础算法
【 6 】分治算法
4. 排序算法
【 5 】归并排序
分治,拆成小份分别排序后合并
【 5 】快速排序
【 6 】堆排序
放到叶子结点然后往上交换位置
【 5 】桶排序
【 6 】基数排序
5. 字符串相关算法
【 5 】字符串匹配:KMP算法
6. 搜索算法
【 6 】搜索的剪枝优化
咕
【 6 】记忆化搜索
咕
【 7 】启发式搜索
搞一个高贵的估价函数来判断剪枝,感觉跟前面的剪枝优化是一个东西啊(
【 7 】双向广度优先搜索
从起点和终点两头同时进行搜索,如果碰到了就是求出解了
主要用处是把暴力搜索的指数减半
【 7 】迭代加深搜索
dfs里多记一层层数,一层一层往下找,优化了dfs深度太深的麻烦,也不会有bfs队列空间占用过大的痛苦
7. 图论算法
【 6 】最小生成树:Prim和Kruskal等算法
Kruskal:从小到大贪心加边
P3366 【模板】最小生成树(Kruskal)
const int N = 200010;
int n, m;
int u[N], v[N], w[N], id[N];
bool cmp(int x, int y) {return w[x] < w[y];}
struct DS {
int fa[N], sz[N];
void build() {for (int i = 1; i <= n; i++) fa[i] = i, sz[i] = 1;}
int root(int x) {return fa[x] == x ? x : fa[x] = root(fa[x]);}
void merge(int x, int y) {
x = root(x), y = root(y);
if (x == y) return ;
if (sz[x] < sz[y]) swap(x, y);
fa[y] = x, sz[x] += sz[y];
}
bool check(int x, int y) {return root(x) == root(y);}
} ds;
ll ans;
int main() {
n = read(), m = read();
ds.build();
for (int i = 1; i <= m; i++) u[i] = read(), v[i] = read(), w[i] = read(), id[i] = i;
sort(id + 1, id + 1 + m, cmp);
for (int i = 1; i <= m; i++) {
int x = u[id[i]], y = v[id[i]];
if (ds.check(x, y)) continue;
ans += w[id[i]];
ds.merge(x, y);
}
int mx = -1;
for (int i = 1; i <= n; i++) mx = max(mx, ds.sz[i]);
if (mx < n) puts("orz");
else printf("%lld\n", ans);
return 0;
}
Prim:从小到大贪心加点
P3366 【模板】最小生成树(Prim)
const int N = 200010;
const ll INF = 1e17;
int n, m;
int last[N], cnt;
struct edge {
int to, next, w;
} e[N << 1];
void addedge(int x, int y, int w) {
e[++cnt].to = y;
e[cnt].next = last[x];
e[cnt].w = w;
last[x] = cnt;
}
priority_queue <pii, vector<pii>, greater<pii> > q;
bool vis[N];
ll dis[N], ans;
void prim(int s) {
for (int i = 1; i <= n; i++) dis[i] = INF;
dis[s] = 0;
q.push({0, s});
while (q.size()) {
pii px = q.top(); q.pop();
int x = px.second;
if (vis[x]) continue;
vis[x] = 1;
ans += dis[x];
for (int i = last[x]; i; i = e[i].next) {
int v = e[i].to;
if (vis[v]) continue;
if (dis[v] > e[i].w) {
dis[v] = e[i].w;
q.push({dis[v], v});
}
}
}
}
int main() {
n = read(), m = read();
for (int i = 1; i <= m; i++) {
int x = read(), y = read(), w = read();
addedge(x, y, w), addedge(y, x, w);
}
prim(1);
bool flag = 0;
for (int i = 1; i <= n; i++)
if (!vis[i]) flag = 1;
if (flag) puts("orz");
else printf("%lld\n", ans);
return 0;
}
【 7 】次小生成树
非严格次小生成树:无向图中,边权和最小的满足边权和大于等于最小生成树边权和的生成树
遍历每条没被选进最小生成树的边 \(e\),用它替换两端 \(u\),\(v\) 在最小生成树上的路径中最长的那条边 \(e'\)
边权最大值用倍增维护
代码咕
严格次小生成树:无向图中,边权和最小的满足边权和严格大于最小生成树边权和的生成树
对求严格次小生成树的办法进行少许修改,倍增维护边权最大值的时候严格维护次大边权,当最大值与 \(e\) 相等的时候用严格次大边权替换
代码还是咕
【 6 】单源最短路:Bellman-Ford、Dijkstra、SPFA 等算法
Bellman-Ford:基于松弛的最短路算法,可以求出有负权的图的最短路、判断最短路是否存在
代码咕
SPFA: Bellman-Ford的一种队列优化。把上一次被松弛的节点连接的所有点丢进队列里,显然它们连接的边才可能在下一次被松弛。
代码咕
Dijkstra:把目前算出来最近的点加进最短路然后继续松弛
P4779 【模板】单源最短路径(标准版)
const int N = 100010, INF = 2e9;
int n, m, s;
int last[N], cnt;
struct edge {
int to, next, w;
} e[N << 1];
void addedge(int x, int y, int w) {
e[++cnt].to = y;
e[cnt].next = last[x];
e[cnt].w = w;
last[x] = cnt;
}
bool vis[N];
int dis[N];
priority_queue <pii, vector<pii>, greater<pii> > q;
void dij(int s) {
for (int i = 1; i <= n; i++) dis[i] = INF;
dis[s] = 0;
q.push({dis[s], s});
while (q.size()) {
pii px = q.top(); q.pop();
int x = px.second;
if (vis[x]) continue;
vis[x] = 1;
for (int i = last[x]; i; i = e[i].next) {
int v = e[i].to;
if (vis[v]) continue;
if (dis[x] + e[i].w < dis[v]) {
dis[v] = dis[x] + e[i].w;
q.push({dis[v], v});
}
}
}
}
int main() {
n = read(), m = read(), s = read();
for (int i = 1; i <= m; i++) {
int u = read(), v = read(), w = read();
addedge(u, v, w);
}
dij(s);
for (int i = 1; i <= n; i++) printf("%d ", dis[i]);
return 0;
}
【 7 】单源次短路
啊?怎么找不到词条?晚点补吧……
【 6 】Floyd-Warshall 算法
计算有向图中任意两点之间的最短路径
啊?没了?晚点补吧……
【 6 】有向无环图的拓扑排序
把图画出来,吐出入度为零的点然后删掉所有和它连着的边
B3644 【模板】拓扑排序 / 家谱树
const int N = 110;
int n;
vector <int> g[N];
int deg[N];
queue <int> q;
int main() {
n = read();
for (int i = 1; i <= n; i++) {
int x = read();
while (x != 0) {
deg[x]++, g[i].emplace_back(x);
x = read();
}
}
for (int i = 1; i <= n; i++)
if (deg[i] == 0) q.push(i);
while (q.size()) {
int x = q.front(); q.pop();
printf("%d ", x);
for (int v: g[x]) {
if (deg[v] == 0) continue;
deg[v]--;
if (deg[v] == 0) q.push(v);
}
}
return 0;
}
【 6 】欧拉道路和欧拉回路
欧拉回路:通过图中每条边一次的回路
欧拉通路:通过图中每条边一次的通路
【 6 】二分图的判定
dfs或bfs判断有无奇环
【 7 】强连通分量
强连通:图中任意两点联通
强连通分量:极大的强连通子图
Tarjan咕
【 7 】割点、割边
割点:删去这个点后极大联通分量数量增加
割边:删去这条边后极大联通分量数量增加
还是要Tarjan咕
【 6 】树的重心、直径、DFS序与欧拉序
树的重心:嗯对,重心。dfs记子树大小
树的直径:嗯对,直径。两遍dfs。
dfs序:dfs时访问结点的顺序
欧拉序:进dfs的时候盖个戳,出的时候盖个戳,排成的序列
【 6 】树上差分、子树和与倍增
【 6 】最近公共祖先
稍微糊了糊
P3379 【模板】最近公共祖先(LCA)(倍增随机TLE(?))
const int N = 500010;
int n, m, s;
vector <int> g[N];
int dep[N], fa[21][N];
void dfs(int x, int from) {
dep[x] = dep[from] + 1, fa[0][x] = from;
for (int v: g[x]) {
if (v == from) continue;
dfs(v, x);
}
}
int lca(int x, int y) {
if (dep[x] > dep[y]) swap(x, y);
for (int i = 20; i >= 0; i--)
if (dep[x] <= dep[fa[i][y]]) y = fa[i][y];
for (int i = 20; i >= 0; i--)
if (fa[i][x] != fa[i][y]) x = fa[i][x], y = fa[i][y];
if (x != y) x = fa[0][x];
return x;
}
int main() {
n = read(), m = read(), s = read();
for (int i = 1; i < n; i++) {
int x = read(), y = read();
g[x].emplace_back(y), g[y].emplace_back(x);
}
dfs(s, s);
for (int i = 1; i <= 20; i++)
for (int j = 1; j <= n; j++)
fa[i][j] = fa[i - 1][fa[i - 1][j]];
for (int i = 1; i <= m; i++) {
int x = read(), y = read();
printf("%d\n", lca(x, y));
}
return 0;
}
8. 动态规划
【 6 】树型动态规划
【 7 】状态压缩动态规划
【 8 】动态规划的常用优化
2.2.5 数学与其他
1. 初等数学
作为高中生就不复习这些了吧(
【 5 】代数(高中部分)
咕
【 6 】几何(高中部分)
咕
2. 初等数论
【 5 】同余式
【 7 】欧拉定理和欧拉函数
【 7 】费马小定理
【 7 】威尔逊定理
【 6 】多重集上的组合
【 6 】错排列、圆排列
【 6 】鸽巢原理
【 6 】二项式定理
【 7 】容斥原理
【 7 】卡特兰(Catalan)数
4. 线性代数
【 5 】向量与矩阵的概念
【 6 】向量的运算
【 6 】矩阵的初等变换
【 6 】矩阵的运算:加法、减法、乘法与转置
【 7 】裴蜀定理
【 7 】模运算意义下的逆元
【 7 】扩展欧几里得算法
【 7 】中国剩余定理
3. 离散与组合数学
【 6 】多重集合
【 6 】等价类
【 6 】多重集上的排列
【 6 】特殊矩阵的概念:单位阵、三角阵、对称阵和稀疏矩阵
【 7 】高斯消元法