constint N = 40010, M = 2*N; int depth[N], f[N][16], h[N], e[M], ne[M], idx;
voidadd(int a, int b){ e[idx] = b, ne[idx] = h[a], h[a] = idx++; }
voiddfs(int u, int father){ depth[u] = depth[father] + 1; f[u][0] = father; for (int k = 1; k <= 15; k++) f[u][k] = f[f[u][k-1]][k-1]; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (!depth[j]) dfs(j, u); } }
intlca(int a, int b){ // 保证 a 比 b 深 if (depth[a] < depth[b]) swap(a, b); for (int k = 15; k >= 0; k--) { if (depth[f[a][k]] >= depth[b]) a = f[a][k]; } if (a == b) return a; for (int k = 15; k >= 0; k--) { if (f[a][k] != f[b][k]) a = f[a][k], b = f[b][k]; } return f[a][0]; }
intmain(){ int n, m, root; memset(h, -1, sizeof h); cin >> n; while (n--) { int a, b; cin >> a >> b; if (b == -1) root = a; elseadd(a, b), add(b, a); } dfs(root, 0); cin >> m; while (m--) { int a, b; cin >> a >> b; int p = lca(a, b); if (p == a) puts("1"); elseif (p == b) puts("2"); elseputs("0"); } return0; }
voidbfs(int root){ queue<int> q; q.push(root); memset(dist, 0x3f, sizeof dist); dist[root] = 0; while (q.size()) { int t = q.front(); q.pop(); for (int i = h[t]; ~i; i = ne[i]) { int j = e[i]; if (dist[j] > dist[t] + w[i]) { dist[j] = dist[t] + w[i]; q.push(j); } } } }
voidtarjan(int u){ mark[u] = 1; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (mark[j] == 0) { tarjan(j); // 这里写不写 find(j) 都无所谓, p[j] 一定等于 j p[j] = u; } } for (pii q : query[u]) { int x = q.first, index = q.second; if (mark[x] == 2) { res[index] = dist[u] + dist[x] - 2*dist[find(x)]; } } // 已回溯 mark[u] = 2; }
intmain(){ int n, m; memset(h, -1, sizeof h); cin >> n >> m; for (int i = 1; i <= n; i++) p[i] = i; for (int i = 0; i < n-1; i++) { int a, b, c; cin >> a >> b >> c; add(a, b, c), add(b, a, c); } bfs(1); for (int i = 0; i < m; i++) { int a, b; cin >> a >> b; if (a != b) { query[a].push_back({b, i}); query[b].push_back({a, i}); } } tarjan(1); for (int i = 0; i < m; i++) cout << res[i] << endl; return0; }
次小生成树
给定一张 N 个点 M 条边的无向图,求无向图的严格次小生成树。
设最小生成树的边权之和为 sum,严格次小生成树就是指边权之和大于 sum 的生成树中最小的一个。
输入格式
第一行包含两个整数 N 和 M。
接下来 M 行,每行包含三个整数 x,y,z,表示点 x 和点 y 之前存在一条边,边的权值为 z。
typedeflonglong ll; constint N = 1e5+10, M = 3e5+10, INF = 0x3f3f3f3f; int n, m; int h[N], e[2*N], ne[2*N], w[2*N], idx; int p[N], d1[N][17], d2[N][17], depth[N], fa[N][17];
structEdge { int a, b, w; bool used; booloperator<(const Edge& e) const { return w < e.w; } } edge[M];
voidadd(int a, int b, int c){ e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; }
// 求最小生成树长度的同时建图 ll kruskal(){ ll sum = 0; for (int i = 1; i <= n; i++) p[i] = i; memset(h, -1, sizeof h); for (int i = 0; i < m; i++) { int a = edge[i].a, b = edge[i].b, w = edge[i].w; int pa = find(a), pb = find(b); if (pa != pb) { p[pa] = pb; sum += w; edge[i].used = true; add(a, b, w), add(b, a, w); } } return sum; }
// 预处理 LCA 需要的数据 voidbfs(){ queue<int> q; q.push(1); memset(depth, 0x3f, sizeof depth); depth[0] = 0, depth[1] = 1; while (q.size()) { int t = q.front(); q.pop(); for (int i = h[t]; ~i; i = ne[i]) { int j = e[i]; if (depth[j] > depth[t] + 1) { depth[j] = depth[t] + 1; q.push(j); fa[j][0] = t, d1[j][0] = w[i], d2[j][0] = -INF; for (int k = 1; k <= 16; k++) { int mid = fa[j][k-1]; fa[j][k] = fa[mid][k-1]; int d[4] = {d1[j][k-1], d2[j][k-1], d1[mid][k-1], d2[mid][k-1]}; for (int u = 0; u < 4; u++) { if (d[u] > d1[j][k]) d2[j][k] = d1[j][k], d1[j][k] = d[u]; elseif (d[u] < d1[j][k] && d[u] > d2[j][k]) d2[j][k] = d[u]; } } } } } }
intlca(int a, int b, int w){ staticint d[4*N]; int cnt = 0; if (depth[a] < depth[b]) swap(a, b); for (int k = 16; k >= 0; k--) { if (depth[fa[a][k]] >= depth[b]) { d[cnt++] = d1[a][k]; d[cnt++] = d2[a][k]; a = fa[a][k]; } } if (a != b) { for (int k = 16; k >= 0; k--) { if (fa[a][k] != fa[b][k]) { d[cnt++] = d1[a][k]; d[cnt++] = d2[a][k]; d[cnt++] = d1[b][k]; d[cnt++] = d2[b][k]; a = fa[a][k], b = fa[b][k]; } } // 还要向上跳一级, d2[x][0] 恒等于 -INF d[cnt++] = d1[a][0], d[cnt++] = d1[b][0]; } int dist1 = -INF, dist2 = -INF; for (int i = 0; i < cnt; i++) { if (d[i] > dist1) dist2 = dist1, dist1 = d[i]; elseif (d[i] < dist1 && d[i] > dist2) dist2 = d[i]; } if (w > dist1) return w-dist1; if (w > dist2) return w-dist2; return INF; }
intmain(){ cin >> n >> m; for (int i = 0; i < m; i++) { int a, b, c; cin >> a >> b >> c; edge[i] = {a, b, c}; } sort(edge, edge+m); ll sum = kruskal(); bfs(); ll ans = 1e18; for (int i = 0; i < m; i++) { if (!edge[i].used) { int a = edge[i].a, b = edge[i].b, w = edge[i].w; ans = min(ans, sum + lca(a, b, w)); } } cout << ans << endl; return0; }
换根 LCA
给定一颗 n 个点的无根树,给 q 组询问,每组询问包含 (u,v,w),求以 w 为根意义下的 LCA(u,v)
为了方便输入,第二行的第 i 个点表示 i 的父结点,保证 fa(1)=0,即给出的是以 1 为根的树。
constint N = 10010, M = 20010; int h[N], e[M], ne[M], idx; int fa[N][15], depth[N]; int n;
voidadd(int a, int b){ e[idx] = b, ne[idx] = h[a], h[a] = idx++; }
voiddfs(int u, int f){ depth[u] = depth[f] + 1; fa[u][0] = f; for (int k = 1; k < 15; k++) fa[u][k] = fa[fa[u][k-1]][k-1];
for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (j == f) continue; dfs(j, u); } }
intlca(int a, int b){ if (depth[a] < depth[b]) swap(a, b); for (int k = 14; k >= 0; k--) { if (depth[fa[a][k]] >= depth[b]) a = fa[a][k]; } if (a == b) return a; for (int k = 14; k >= 0; k--) { if (fa[a][k] != fa[b][k]) a = fa[a][k], b = fa[b][k]; } return fa[a][0]; }
intLCA(int u, int v, int w){ int p = lca(u, v), a = lca(w, u), b = lca(w, v); if (a == b) return p; if (depth[a] > depth[b]) return a; return b; }
intmain(){ memset(h, -1, sizeof h); scanf("%d", &n); for (int i = 1, j; i <= n; i++) { scanf("%d", &j); if (j) add(i, j), add(j, i); } int q, u, v, w; scanf("%d", &q); dfs(1, 0); while (q--) { scanf("%d%d%d", &u, &v, &w); printf("%d\n", LCA(u, v, w)); } return0; }
intmain(){ srand(20230930); int n = rand() % 10000 + 1; fa[1] = 0; for (int i = 2; i <= n; i++) { int f; fa[i] = rand() % (i-1) + 1; } printf("%d\n", n); for (int i = 1; i <= n; i++) printf("%d ", fa[i]); printf("\n"); int q = rand() % 10000 + 1; printf("%d\n", q); for (int i = 1, u, v, w; i <= q; i++) { u = rand() % n + 1, v = rand() % n + 1, w = rand() % n + 1; printf("%d %d %d\n", u, v, w); } return0; }