洛谷P1967 货车运输

传送门
不愧是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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值