jzoj5914. 【NOIP2018模拟10.19】盟主的忧虑(并查集)

5914. 【NOIP2018模拟10.19】盟主的忧虑

Description
江湖由 N 个门派(2≤N≤100,000,编号从 1 到 N)组成,这些门派之间有 N-1 条小道将他们连接起来,每条道路都以“尺”为单位去计量,武林盟主发现任何两个门派都能够直接或者间接通过小道连接。
虽然整个江湖是可以互相到达的,但是他担心有心怀不轨之徒破坏这个武林的安定,破坏小道,于是武林盟主又秘密地修建了 M 条密道(1≤M≤100,000),但每条小道距离都不超过10亿尺。
果不其然,最近一个名叫“太吾”的组织意欲破坏武林的小道,请你帮盟主想想办法,如果门派 A 到门派 B 的直连小道被破坏,从 A 走到 B 的所有路径中,经过密道的距离最少是多少?

Input
第一行数字 N M
接下来 N-1 行,每行两个整数 A B,表示 A-B 间有一条直连小道
接下来 M 行,每行三个数字 A B V,表示 A-B 间有一条代价为 V 的密道

Output
输出 N-1 行,对应原输入的小道,每个小道被破坏后,最少需要经过多长的密道?如果不存在替换的道路,请输出-1

Sample Input
6 3
4 1
1 3
4 5
1 2
6 5
3 6 8
2 3 7
6 4 5

Sample Output
8
7
5
7
5

Data Constraint
30%数据:N<=300,M<=1000
50%数据:N<=1000,M<=1000
70%数据:N<=5000,M<=5000
对于另外15%的数据点:树是一条链
100%数据:N,M<=100000

分析:将所有密道按照权值从小到大排序。
对于一条密道(u,v,w),如果u到v的路径上的边中存在边之前还没有被覆盖过,那么说明这条边的答案就是w.
可以用并查集维护,维护每个集合深度最小的节点,对于密道(u,v,w),每次u都在它所在集合中找到深度最小的
点,这个点与父亲的连边一定就是上述的边,将这条边的答案更新为w,然后将这个节点与其父亲合并,直到u所
在集合的深度最小的节点是小于u和v的lca的,对v做同样的过程即可。

代码

#include <cstdio>
#include <algorithm>
#define ll long long
#define N 300000
using namespace std;

struct arr
{
	int nxt,to,num;
}a[N];
struct node
{
	int x,y;
	ll z;
}b[N];
int fa[N],mn[N],ls[N],c[N];
int dep[N],n,m,l;
ll ans[N];
int f[N][30];

int cmp(node p, node q){return p.z < q.z;}
void add(int x, int y, int i)
{
	a[++l].to = y;
	a[l].nxt = ls[x];
	a[l].num = i;
	ls[x] = l;
}

void dfs(int x, int father)
{
	f[x][0] = father;
	for (int i = ls[x]; i; i = a[i].nxt)
	if (a[i].to != father)
	{
		dep[a[i].to] = dep[x] + 1;
		c[a[i].to] = a[i].num;
		dfs(a[i].to, x);
	}
}

int find(int x)
{
	if (fa[x] == x) return x;
	return fa[x] = find(fa[x]);
}

int main()
{
	freopen("worry0.in","r",stdin);
//	freopen("worry.out","w",stdout);
	scanf("%d%d", &n, &m);
	for (int i = 1; i < n; i++)	
	{
		int x, y;
		scanf("%d%d", &x, &y);
		add(x, y, i);
		add(y, x, i);
	}
	dep[1] = 1;
	dfs(1, 0);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= 25; j++)
			f[i][j] = f[f[i][j - 1]][j - 1];
	for (int i = 1; i <= n; i++)
		fa[i] = i;
	for (int i = 1; i <= m; i++)
		scanf("%d%d%lld", &b[i].x, &b[i].y, &b[i].z);
	sort(b + 1, b + m + 1, cmp);
	for (int i = 1; i <= m; i++)
	{
		int u = find(b[i].x);
		int v = find(b[i].y);
		while (u != v) 
		{
			if (dep[u] < dep[v]) swap(u, v);
			ans[c[u]] = b[i].z;
			u = fa[u] = find(f[u][0]);
		}
	}
	for (int i = 1; i < n; i++)
		if (!ans[i]) printf("-1\n");
			else printf("%lld\n", ans[i]);
}                                                                                                                     
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值