【JZOJ A组】盟主的忧虑

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<=100,000

思路

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

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e5+77;
int n,m,dep[maxn],list[maxn],f[maxn],f1[maxn],d[maxn],p[maxn],cnt;
struct A
{
	int x,y,v;
}a[maxn];
struct E
{
	int to,next,id;
}e[maxn*2];
bool cmp(A x,A y)
{
	return x.v<y.v;
}
void add(int u,int v,int id)
{
	e[++cnt].to=v; e[cnt].next=list[u]; list[u]=cnt; e[cnt].id=id;
}
int gf(int x)
{
	return f[x]?f[x]=gf(f[x]):x;
}
void dfs(int u,int fa)
{
	f1[u]=fa; dep[u]=dep[fa]+1;
	for(int i=list[u]; i; i=e[i].next)
	{
		int v=e[i].to;
		if(v==fa) continue;
		p[v]=e[i].id; dfs(v,u);
	}
}
void doit(int &x,int y)
{
	d[p[x]]=y; f[x]=gf(f1[x]); x=f[x];
}
int main()
{
	freopen("worry.in","r",stdin),freopen("worry.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1,x,y; i<=n-1; i++) scanf("%d%d",&x,&y),add(x,y,i),add(y,x,i),d[i]=-1;
	for(int i=1; i<=m; i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
	dfs(1,0); sort(a+1,a+m+1,cmp);
	for(int i=1; i<=m; i++)
	{
		int u=gf(a[i].x),v=gf(a[i].y);
		while(u!=v) doit(dep[u]>dep[v]?u:v,a[i].v);
	}
	for(int i=1; i<=n-1; i++) printf("%d\n",d[i]);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值