【图论2-1】基础树上问题

LCA 最近公共祖先/并查集

P5836 [USACO19DEC]Milk Visits S
图片说明
这里我参考的是最后一个暴力模拟。。因为比较好懂。。但是会有一个TLE。
https://www.luogu.com.cn/problem/solution/P1099?page=5

P3379 【模板】最近公共祖先(LCA)
图片说明

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;
const int N = 5e5 + 10, M = 2 * N; //N是点数,M是边数
const int INF = 0x3f3f3f3f;

int n, m, s, a, b;
int h[N], e[M], ne[M], idx;
//h[i]是每个点对应的邻接链表 e[i]每条边所指向的点 ne[i]每条边的下一条边 idx表示第几条边
int d[N], f[N][30];   //点的深度  f[u][k]表示u的2^k辈祖先,即从u向根节点走 2^k步到达的节点
bool st[N]; //记录每个点是否已被遍历过

void add(int a, int b){
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

// 处理出各个点的深度 
void dfs(int u, int dep){
    d[u] = dep;
    for(int i = 0; i < 20; i++){
        f[u][i + 1] = f[f[u][i]][i];
    }
    for(int i = h[u]; i != -1; i = ne[i]){
        int j = e[i];
        if(!d[j]){
            f[j][0] = u;
            dfs(j, dep + 1);
        }
    } 
}

int LCA(int x, int y){
    if(d[x] < d[y]) swap(x, y);
    for(int i = 20; i >= 0; i--){
        if(d[f[x][i]] >= d[y]) x = f[x][i];
        if(x == y) return x;
    }
    for(int i = 20; i >= 0; i--){
        if(f[x][i] != f[y][i]) {
            x = f[x][i];
            y = f[y][i];
        }
    }
    return f[x][0];
}

int main(){
    ios::sync_with_stdio(false);
    cin >> n >> m >> s; 
    memset(h, -1, sizeof(h));
    for(int i = 1; i < n; i++){
        cin >> a >> b;
        add(a, b);
        add(b, a);
    }
    f[s][0] = s;
    dfs(s, 1);
    while(m--){
        cin >> a >> b;
        cout << LCA(a, b) << endl;
    }
    return 0;
}

P3398 仓鼠找sugar
图片说明

P4281 [AHOI2008]紧急集合 / 聚会
图片说明

数的重心

P1395 会议
https://www.luogu.com.cn/blog/TheBlogOfNothingness/solution-p1395
图片说明
acwing 树与图的深度优先遍历解决的就是树的重心问题。
这里我结合了y总的求树的重心的模板和题解的bfs。

【图论2-2】最短路

Floyed

P1119 灾后重建
深入理解floyed中k的含义。Floyd算法就是一个利用其它点k进行中转来求最短路的步骤。
这里每次输入时间使得村庄重建完成相当于k被更新。

spfa

P3371 【模板】单源最短路径(弱化版)

Dijkstra

P4779 【模板】单源最短路径(标准版)
使用spfa会超时。
图片说明
图片说明
改用dijkstra
图片说明