Gym - 101889I Imperial roads 树链剖分+最小生成树之必选一边

题目链接:https://vjudge.net/problem/Gym-101889I

题意:n个点,m条边,q次询问,每次必须选一边,求最小生成树的权值

题解:我们先跑一边最小生成树,然后按照最小边建树,必选某条边的话,如果在树上,那就直接输出,如果不在,那么在树上加上这条边肯定会形成一个环,那就在环中去掉一条权值最大的,那么路径查询树剖一下即可

#include <bits/stdc++.h>
#include <tr1/unordered_map>
using namespace std;
const int N = 1e5 + 10;
struct edge {
	int x, y, z;
	bool operator <(const edge &xx)const {
		return z < xx.z;
	}
}e[N * 2];
struct node {
	int l, r;
	int maxx;
}tree[N << 2];
map<pair<int, int>, int> mp;
vector<pair<int, int> > v[N];
int n, m, q;
int f[N];
int in[N], p[N], son[N], snum[N];
int dep[N], root[N], ff[N];
int val[N];
int tot;
int fath(int x) {
	return x == f[x] ? f[x] : f[x] = fath(f[x]);
}
void dfs(int u, int fa) {
	dep[u] = dep[fa] + 1;
	ff[u] = fa;
	snum[u] = 1;
	int to, z;
	for(int i = 0; i < v[u].size(); i++) {
		to = v[u][i].first;
		z = v[u][i].second;
		if(to == fa) continue;
		dfs(to, u);
		val[to] = val[u] + z;
		snum[u] += son[to];
		if(snum[son[u]] < snum[to])
			son[u] = to;
	}
}
void dfs1(int u, int fa, int rt) {
	in[u] = ++tot;
	p[tot] = u;
	root[u] = rt;
	if(son[u]) dfs1(son[u], u, rt);
	int to;
	for(int i = 0; i < v[u].size(); i++) {
		to = v[u][i].first;
		if(to == fa || to == son[u]) continue;
		dfs1(to, u, to);
	}
}
void build(int l, int r, int cur) {
	tree[cur].l = l;
	tree[cur].r = r;
	if(l == r) {
		tree[cur].maxx = val[p[l]];
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, cur << 1);
	build(mid + 1, r, cur << 1 | 1);
	tree[cur].maxx = max(tree[cur << 1].maxx, tree[cur << 1 | 1].maxx);
}
int query(int pl, int pr, int cur) {
	if(pl <= tree[cur].l && tree[cur].r <= pr) {
		return tree[cur].maxx;
	}
	int res = 0;
	if(pl <= tree[cur << 1].r) res = max(res, query(pl, pr, cur << 1));
	if(pr >= tree[cur << 1 | 1].l) res = max(res, query(pl, pr, cur << 1 | 1));
	return res;
}
int solve(int x, int y) {
	int fx = root[x], fy = root[y];
	int res = 0;
	while(fx != fy) {
		if(dep[fx] < dep[fy]) {
			swap(x, y);
			swap(fx, fy);
		}
		res = max(res, query(in[fx], in[x], 1));
		x = ff[fx];
		fx = root[x];
	}
	if(dep[x] > dep[y]) swap(x, y);
	if(in[x] < in[y]) res = max(res, query(in[x] + 1, in[y], 1));
	return res;
}
int main() {
	int ans;
	int x, y;
	int xx, yy;
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= m; i++) {
		scanf("%d %d %d", &e[i].x, &e[i].y, &e[i].z);
		mp[make_pair(e[i].x, e[i].y)] = e[i].z;
	}
	sort(e + 1, e + 1 + m);
	ans = 0;
	for(int i = 1; i <= n; i++) f[i] = i;
	for(int i = 1; i <= m; i++) {
		xx = fath(e[i].x);
		yy = fath(e[i].y);
		if(xx == yy) continue;
		f[xx] = yy;
		ans += e[i].z;
		v[e[i].x].push_back(make_pair(e[i].y, e[i].z));
		v[e[i].y].push_back(make_pair(e[i].x, e[i].z));
		mp[make_pair(e[i].x, e[i].y)] = -1;
	}
	dfs(1, 0);
	dfs1(1, 0, 1);
	build(1, n, 1);
	scanf("%d", &q);
	while(q--) {
		scanf("%d %d", &x, &y);
		if(mp[make_pair(x, y)] == -1) printf("%d\n", ans);
		else {
			printf("%d\n", ans - solve(x, y) + mp[make_pair(x, y)]);
		} 
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值