Gym - 101889I Imperial roads (LCA倍增+最小生成树思想)

                                                                  Problem I – Imperial roads
Author : Edwin Ni˜no, Colombia
The roads of Cubiconia are in a dire state, after years of neglect and lack of maintenance. Each road
connects two different cities A and B and can be traveled in both ways (from A to B or from B to
A). There is at most one road between each pair of cities, and using the existing roads it is possible
to travel between any pair of cities. The new emperor of Cubiconia has just raised the taxes (again!),
but promised to repair at least some of the roads, guaranteeing that Cubiconians will be able to travel
between any pair of cities using only restored roads.
The Department of Public Works have calculated the cost of repairing each individual road. Now
they want to calculate the minimum cost for repairing a set of roads so that the emperor’s promise
is made true. This is not easy because the emperor wants the set of repaired roads to include one
particular road, but he has not yet decided which particular road to include: could be the one that
connects the city where his castle is to the city where his daughter’s royal residence is, or the road that
connects the city where his summer palace is to the only city by the seaside, or. . . Fearing the emperor
will take too long to decide, the engineers want your help.
Given the description of the roads in Cubiconia, with their respective repairing costs, you must write
a program to answer a set of queries. For each query you will be given one specific road that should be
repaired, and must determine the minimum cost for repairing a set of roads (including the given specific
road) so that Cubiconians will be able to travel between any pair of cities using only restored roads.
Input
The first line contains two integers N (2 ≤ N ≤ 105
) and R (N − 1 ≤ R ≤ 2 × 105
), representing
respectively the number of cities and the number of roads in Cubiconia. Cities are identified by distinct
integers from 1 to N. Each of the next R lines describes a road with three integers A, B (1 ≤ A < B ≤ N)
and C (1 ≤ C ≤ 104
), indicating that there is a road between cities A and B and the cost of repairing
it is C. There is at most one road between each pair of cities, and using the existing roads it is
possible to travel between any pair of cities. The next line contains an integer Q (1 ≤ Q ≤ 105
)
representing the number of queries. Each of the next Q lines describes a query with two integers U
and V (1 ≤ U < V ≤ N), indicating the specific road that should be repaired. There are no repeated
queries.
Output
Output Q lines, each line with an integer indicating the answer to the corresponding query of the
input, that is, the minimum cost for repairing a set of roads (including the specific road in the query)
so that Cubiconians will be able to travel between any pair of cities using only restored roads.

 

Sample input 1
3 3
1 2 10
2 3 5
1 3 7
3
2 3
1 2
1 3
Sample output 1
12
15
12
Sample input 2
4 4
1 2 1
2 4 1
2 3 100
1 4 50
1
1 4
Sample output 2
151

Sample input 3
5 7
1 2 8
1 3 10
2 4 5
2 3 12
4 5 4
3 5 14
1 5 20
3
2 3
1 5
3 5
Sample output 3
29
39
31

一、原题地址

点我传送

 

二、大致题意

给出n个点,m条路把他们相连。下面给出Q个询问,每个询问中给定一条边(u,v),要求(u,v)这条边必须在最小生成树中的情况下,求最小生成树值。

 

三、思路

每当一条边被要求必须加入最小生成树上的时候,有两种情况。

1、这条边就在原图的最小生成树中,那么输出的答案就是原图的最小生成树值 

2、这条边不在原图的最小生成树内,那么这条边的加入就相当于将这个原本就在一个集合内的两个点u,v又并到一起了,这显然是多余的操作,所以只要找到原本把这两个点连起来的那些边,试图打断掉一条。这样就仍能保证这两个点在同一个集合,且与他们连接的点也仍能通过这一条新的边被并在一起。

这时的答案应该=原图的最小生成树 + 这条新边 - 一条可以被打断的边。

为了保证我们所得的答案最小,显然这个被打断的边也就越大越好。

情况一是很好处理的,对于情况二问题就变成了在一棵树上两点(u,v)之间找一条最大的边。这里使用LCA倍增法来在线的寻找这个最值边。

 

四、代码

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<map>
using namespace std;
const int inf = (1 << 30) - 1;
const long long int INF = 1e18;
typedef long long LL;


const int MAXN = 100005, MAXM = 200005;
struct Edge
{
	int from, v, next, cost;
	bool operator < (const Edge a)const
	{
		return cost<a.cost;
	}
}e[MAXM],ee[MAXM];  //e用于记录所有的边,ee用于记录生成树上的边
int head[MAXM], dist[MAXN];
int dep[MAXN], pa[MAXN][25],maxx[MAXN][25];
int ind, n, m, q;
bool v[MAXN];
map<pair<int, int>, int>vis, dd;		//vis用于标记在生成树上使用的边
										//dd用于记录边的权值

void add(int from, int to, int cost)
{
	e[ind].next = head[from];
	e[ind].v = to;
	e[ind].cost = cost;
	e[ind].from = from;
	head[from] = ind++;
}

int pre[MAXN];
void init()
{
	ind = 0; maxx[1][0] = 0;
	memset(head, -1, sizeof(head));
	memset(v, false, sizeof(v));
	vis.clear(); dd.clear();
}
int find(int x)
{
	if (x == pre[x])
		return x;
	return pre[x] = find(pre[x]);
}
int Kruskal()
{
	for (int i = 1; i <= n; ++i)pre[i] = i;
	sort(ee, ee + m);
	int ans = 0;
	for (int i = 0; i<m; ++i)
	{
		int u = find(ee[i].from);
		int v = find(ee[i].v);
		if (u != v)
		{
			ans += ee[i].cost;
			vis[make_pair(ee[i].from, ee[i].v)] = inf;
			vis[make_pair(ee[i].v, ee[i].from)] = inf;
			add(ee[i].from, ee[i].v, ee[i].cost);
			add(ee[i].v, ee[i].from, ee[i].cost);
			pre[u] = v;
		}
	}
	return ans;
}

void DFS(int u, int f)
{
	pa[u][0] = f;
	for (int i = head[u]; i != -1; i = e[i].next)
	{
		int to = e[i].v;
		if (to != f)
		{
			dep[to] = dep[u] + 1;
			maxx[to][0] = e[i].cost;
			DFS(to, u);
		}
	}
}
int maxh;
void deal()
{
	int j;
	for (j = 1; (1 << j)<n; j++)
		for (int i = 1; i <= n; i++)
		{
			int t = pa[i][j - 1];
			pa[i][j] = pa[pa[i][j - 1]][j - 1];
			maxx[i][j] = max(maxx[i][j - 1], maxx[t][j - 1]);
		}
	maxh = j - 1;
}
int swim(int x, int k, int &ma)   /// 返回 x 的第 k 个祖先
{
	ma = 0;
	for (int i = 0; i <= maxh; i++)
	{
		if (k >> i & 1)
		{
			ma = max(ma, maxx[x][i]);
			x = pa[x][i];
		}
	}
	return x;
}
int LCA(int x, int y)
{
	if (dep[x] > dep[y]) swap(x, y); ///dep[x] <= dep[y];
	int ans = 0;
	y = swim(y, dep[y] - dep[x], ans);

	if (x == y) return ans;
	for (int i = maxh; i >= 0; i--) 
	{
		if (pa[x][i] != pa[y][i])
		{
			ans = max(ans, max(maxx[x][i], maxx[y][i]));
			x = pa[x][i], y = pa[y][i];
		}
	}
	return max(ans, max(maxx[x][0], maxx[y][0])); //f[x][0];
}
int main()
{
	while(scanf("%d %d",&n,&m)!=EOF)
	{
		init();
		for (int i = 0; i < m; i++)
		{
			scanf("%d %d %d", &ee[i].from, &ee[i].v, &ee[i].cost);
			dd[make_pair(ee[i].from, ee[i].v)] = ee[i].cost;
			dd[make_pair(ee[i].v, ee[i].from)] = ee[i].cost;
		}
		int mintree = Kruskal();

		memset(dep, 0, sizeof(dep));
		DFS(1, 0);
		deal();
		scanf("%d", &q);
		while (q--)
		{
			int u, v;
			scanf("%d %d", &u, &v);
			if (vis[make_pair(u, v)] == inf)
			{
				printf("%d\n", mintree);
			}
			else
			{
				printf("%d\n", mintree + dd[make_pair(u, v)] - LCA(u, v));
			}
		}
	}
	getchar(); 
	getchar();
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值