题目来源https://www.luogu.org/problem/show?pid=1967
60分
Kruskal+SPFA
先用Kruskal构造出最大生成树,再在树上跑SPFA,过掉60%的数据。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 50001;
int r[maxn], p[maxn], u[maxn], v[maxn], w[maxn];
bool cmp(const int i, const int j) { return w[i] > w[j]; }
int find(int x) {
if (p[x] == x)return x;
return p[x] = find(p[x]);
}
const int maxm = 1001;
struct edge {
int l, t;
};
vector <edge> a[maxn];
bool inq[maxm];
int dis[maxm][maxm];
const int inf = 1000000000;
int main() {
ios::sync_with_stdio(false);
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int x, y, z;
cin >> x >> y >> z;
u[i] = x;
v[i] = y;
w[i] = z;
}
for (int i = 1; i <= n; i++)p[i] = i;
for (int i = 1; i <= m; i++)r[i] = i;
sort(r + 1, r + 1 + m, cmp);
for (int i = 1; i <= m; i++) {
int e = r[i];
int x = find(v[e]);
int y = find(u[e]);
if (x != y) {
p[x] = y;
edge r;
r.t = y;
r.l = w[e];
a[x].push_back(r);
r.t = x;
a[y].push_back(r);
}
}
int q;
cin >> q;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
dis[i][j] = inf;
}
}
for (int k = 1; k <= q; k++) {
int x, y;
cin >> x >> y;
if (dis[x][y] < inf) {
cout << dis[x][y] << endl;
continue;
}
if (dis[y][x] < inf) {
cout << dis[y][x] << endl;
continue;
}
int dist[maxm];
for (int i = 1; i <= n; i++)dist[i] = inf;
queue<int> Q;
memset(inq, 0, sizeof(inq));
Q.push(x);
inq[x] = 1;
while (!Q.empty()) {
int e = Q.front();
Q.pop();
for (int i = 0; i < a[e].size(); i++) {
if (a[e][i].t == x)continue;
if (dist[a[e][i].t] == inf) {
dist[a[e][i].t] = min(dist[e], a[e][i].l);
if (!inq[a[e][i].t]) {
inq[a[e][i].t] = 1;
Q.push(a[e][i].t);
}
} else if (dist[a[e][i].t] < min(dist[e], a[e][i].l)) {
dist[a[e][i].t] = min(dist[e], a[e][i].l);
if (!inq[a[e][i].t]) {
inq[a[e][i].t] = 1;
Q.push(a[e][i].t);
}
}
}
inq[e] = 0;
}
for (int i = 1; i <= n; i++) {
if (dist[i] == inf)dis[x][i] = -1;
else dis[x][i] = dist[i];
}
cout << dis[x][y] << endl;
}
return 0;
}
100分
Kruskal+LCA
倍增思想。
anc[i][k] 表示从节点i向上走2^k步所到达的节点。
更新:anc[x][i] = anc[anc[x][i - 1]][i - 1]
lcs[i][k] 表示从节点i向上走 2 ^ k 步的过程中经过的权值最小的边。
更新:lcs[x][i] = min(lcs[x][i - 1], lcs[anc[x][i - 1]][i - 1]);
在dfs构造anc[i][k]的同时构造lcs[i][k]。
在线查询节点x,y的lca的同时找出其经过的路径之中边权的最小值。
dep数组表示节点的深度,swim函数是将两节点调到同一深度,并记录经过路径的最小权值。
100分代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10001;
const int maxm = 50001;
const int maxh = 21;
const int inf = 1000000000;
int r[maxm], p[maxm], u[maxm], v[maxm], w[maxm];
struct edge {
int l, t;
};
vector<edge> a[maxn];
int n, m;
int dep[maxn];
int s[maxn]; //stack<int> s;
int anc[maxn][maxh];
int lcs[maxn][maxh];
int wr[maxn];
bool cmp(const int i, const int j) { return w[i] > w[j]; }
int find(int x) {
if (p[x] == x)return x;
return p[x] = find(p[x]);
}
void dfs(int root) {
int top = 0;
dep[root] = 1;
for (int i = 0; i < maxh; i++) {
anc[root][i] = root;
lcs[root][i] = inf;
}
s[++top] = root;
while (top) {
int x = s[top--];
if (x != root)
for (int i = 1; i < maxh; i++) {
anc[x][i] = anc[anc[x][i - 1]][i - 1];
lcs[x][i] = min(lcs[x][i - 1], lcs[anc[x][i - 1]][i - 1]);
}
for (int i = 0; i < a[x].size(); i++) {
int y = a[x][i].t;
if (y != anc[x][0]) {
dep[y] = dep[x] + 1;
anc[y][0] = x;
lcs[y][0] = a[x][i].l;
s[++top] = y;
}
}
while (top && a[s[top]].size() == 0)top--;
}
return;
}
int swim(int &x, int h) {
int tot = inf;
for (int i = 0; h > 0; i++) {
if (h & 1) {
tot = min(tot, lcs[x][i]);
x = anc[x][i];
}
h = (h >> 1);
}
return tot;
}
int lca(int x, int y) {
int pos;
if (dep[x] > dep[y])swap(x, y);
int tot = swim(y, dep[y] - dep[x]);
if (x == y)return tot;
while (1) {
for (pos = 0; anc[x][pos] != anc[y][pos]; pos++);
if (pos == 0) {
tot = min(lcs[y][0], tot);
tot = min(lcs[x][0], tot);
return tot;
}
tot = min(tot, lcs[x][pos - 1]);
x = anc[x][pos - 1];
tot = min(tot, lcs[y][pos - 1]);
y = anc[y][pos - 1];
}
return -1;
}
int main() {
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int x, y, z;
cin >> x >> y >> z;
u[i] = x;
v[i] = y;
w[i] = z;
}
for (int i = 1; i <= n; i++)p[i] = i;
for (int i = 1; i <= m; i++)r[i] = i;
sort(r + 1, r + 1 + m, cmp);
for (int i = 1; i <= m; i++) {
int e = r[i];
int x = find(v[e]);
int y = find(u[e]);
if (x != y) {
p[x] = y;
edge r;
r.t = v[e];
r.l = w[e];
a[u[e]].push_back(r);
r.t = u[e];
a[v[e]].push_back(r);
}
}
int q;
cin >> q;
for (int i = 1; i <= q; i++) {
int x, y;
cin >> x >> y;
int ax = find(x);
int ay = find(y);
if (anc[ax][0] == 0)dfs(ax);
if (anc[ay][0] == 0)dfs(ay);
if (ax != ay)cout << -1 << endl;
else cout << lca(x, y) << endl;
}
return 0;
}