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 】高斯消元法

posted @ 2024-10-20 15:43  shiranui  阅读(9)  评论(0编辑  收藏  举报
*/