传送门
不愧是NOIP2013TG的题目。
solution:
为了让车获得最大的载重,他们走的路径一定是这个图的最大生成树上的路径。
那么建树,问题转化为查询树上两点间路径上每条边的边权的最小值。
这不就是树剖的板子?但是我们发现这并不是维护点权,是维护边权。
仔细思考发现,我们可以将每条边的边权转到其深度较大的点上(即赋值到儿子上)。
只需要在对树进行操作的时候(如用树剖求两点间所经过路径的最小值)要注意避开
L
C
A
(
x
,
y
)
LCA(x, y)
LCA(x,y)这个点。因为这个点代表的是
L
C
A
(
x
,
y
)
LCA(x, y)
LCA(x,y)与其父亲之间的边,不应算入答案内。即在
d
f
s
dfs
dfs序上操作的时候应该不计其深度最低的一点。
当然我们发现,这题并不要求修改边权。那么我们也可以用树上倍增预处理最值。原理与RMQ相像?
code:
// 这题使用RMQ求dfs序上的最小值,反正题目不要求修改。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 100007;
const int LOGN = 20;
const int INF = 99999999;
int N, M, Q;
int value[MAXN];
struct Edge {
int u, v, w, nxt;
bool operator < (const Edge x) const {return w > x.w;}
Edge (int u = 0, int v = 0, int w = 0, int nxt = 0) : u(u), v(v), w(w), nxt(nxt) {}
}e_for[MAXN], e[MAXN];
int fir[MAXN], tote;
int father[MAXN];
int book[MAXN], dep[MAXN], siz[MAXN], son[MAXN], fa[MAXN], top[MAXN], seg[MAXN], rev[MAXN], ti;
int lo[MAXN], f[MAXN][LOGN + 2];
void addedge(int x, int y, int z) {
e[++tote] = Edge(x, y, z, fir[x]);
fir[x] = tote;
}
int find(int x) {
if(father[x] == x) return x;
return father[x] = find(father[x]);
}
void kruscal() {
for(int i = 1; i <= N; i++) father[i] = i;
int tot = 0;
for(int i = 1; i <= M; i++) {
int x = e_for[i].u, y = e_for[i].v;
int fx = find(x), fy = find(y);
if(fx == fy) continue;
father[fx] = fy; father[x] = fy;
tot++;
addedge(x, y, e_for[i].w);
addedge(y, x, e_for[i].w);
if(tot == N - 1) break;
}
}
void dfs1(int x, int fath, int col) {
book[x] = col;
fa[x] = fath;
dep[x] = dep[fath] + 1;
siz[x] = 1;
int mxid = 0, mx = 0;
for(int i = fir[x]; i; i = e[i].nxt) {
int y = e[i].v;
if(y == fath) continue;
dfs1(y, x, col);
if(siz[y] > mx) mxid = y, mx = siz[y];
siz[x] += siz[y];
}
son[x] = mxid;
}
void dfs2(int x, int topr) {
top[x] = topr;
seg[x] = ++ti;
rev[ti] = x;
if(!son[x]) return;
dfs2(son[x], topr);
for(int i = fir[x]; i; i = e[i].nxt) {
int y = e[i].v;
if(y == fa[x] || y == son[x]) continue;
dfs2(y, y);
}
}
void RMQ() {
lo[0] = -1;
for(int i = 1; i <= N; i++)
lo[i] = lo[i >> 1] + 1;
for(int j = 1; j <= LOGN; j++)
for(int i = 1; i <= N; i++)
f[i][j] = INF;
for(int j = 1; j <= LOGN; j++)
for(int i = 1; i + (1 << j) - 1 <= N; i++)
f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
}
int query(int l, int r) {
int k = lo[r - l + 1];
return min(f[l][k], f[r - (1 << k) + 1][k]);
}
int Query(int x, int y) {
if(book[x] != book[y]) return -1;
int mx = INF;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x, y);
mx = min(mx, query(seg[top[x]], seg[x]));
x = fa[top[x]];
}
if(x == y) return mx;
if(dep[x] < dep[y]) swap(x, y);
mx = min(mx, query(seg[y] + 1, seg[x]));
return mx;
}
int main() {
#ifdef test
freopen("test.txt", "r", stdin);
#endif
cin >> N >> M;
int x, y, z;
for(int i = 1; i <= M; i++) {
scanf("%d %d %d", &x, &y, &z);
e_for[i] = Edge(x, y, z, 0);
}
sort(e_for + 1, e_for + M + 1);
kruscal();
int col = 0;
for(int i = 1; i <= N; i++)
if(!book[i])
dfs1(i, i, ++col);
for(int i = 1; i <= N; i++)
if(!top[i])
dfs2(i, i);
for(int i = 1; i <= N * 2; i++) {
x = e[i].u, y = e[i].v;
if(dep[x] < dep[y])
f[seg[y]][0] = e[i].w;
}
RMQ();
cin >> Q;
for(int i = 1; i <= Q; i++) {
scanf("%d %d", &x, &y);
printf("%d\n", Query(x, y));
}
return 0;
}