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

Time Limits: 1000 ms Memory Limits: 131072 KB

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

Solution

Algorithm 1

吐槽一下比赛查询的时候没有用 t i d [ x ] tid[x] tid[x] 的心情。。。 100 100 100 0 0 0 ,心态崩了
这是一棵树,树上任意走代价为 0 0 0 ,删去任意一条小道,这棵树都会被分成两个连通块,一条密道(u,v)对某条边 ( x , y ) (x,y) (x,y) 有贡献当且仅当这条密道联通了被删去 ( x , y ) (x,y) (x,y) 边的后两边的连通块,容易发现,可以获得贡献的边为 u u u v v v l c a ( u , v ) lca(u,v) lca(u,v) 上的边,这些边总是一段一段的,那么用树链剖分维护最小值即可。
时间复杂度 O ( N l o g 2 N ) O(Nlog^2N) O(Nlog2N)

Algorithm 2

极短极快!!!
将所有密道按权值从小到大排序。对于一条密道 ( u , v , w ) (u,v,w) (u,v,w) ,如果 u u u v v v 路径上的边不曾被覆盖过,那么答案显然就是 w w w,可以使用并查集维护每个集合深度最小的结点,对于一条密道,每次 u u u 都在它所在的集合中找到深度最小的结点,这个点与其父亲的连边一定是上述的边,将这条边权值更新,并且与其父亲合并,直到 u u u 所在集合的深度最小的节点是小于 u u u v v v l c a lca lca 的,对 v v v 做同样的过程即可。
时间复杂度 O ( M α ( N ) ) O(M\alpha(N)) O(Mα(N))

Code

Algorithm 1
#include<algorithm>
#include<cstdio>

#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)
#define L x<<1
#define R L|1

using namespace std;

const int N=1e5+5,inf=2147483647;
int n,m,num,cnt,len;
int last[N],tr[4*N],tag[4*N];
int son[N],dep[N],size[N],f[N][20];
int top[N],tid[N],rnk[N];
struct node{int x,y;}a[N];
struct edge{int to,next;}e[2*N];

void link(int x,int y) {e[++num]=(edge){y,last[x]},last[x]=num;}

void dfs1(int x,int fa)
{
	f[x][0]=fa,dep[x]=dep[fa]+1;
	for(int w=last[x];w;w=e[w].next)
	{
		int y=e[w].to;
		if(y==fa) continue;
		dfs1(y,x);
		size[x]+=size[y];
		if(son[x]<0||size[y]>size[son[x]]) son[x]=y;
	}
}

void dfs2(int x,int tp)
{
	rnk[++cnt]=x,tid[x]=cnt,top[x]=tp;
	if(son[x]<0) return;
	dfs2(son[x],tp);
	for(int w=last[x];w;w=e[w].next)
	{
		int y=e[w].to;
		if(y==f[x][0]||y==son[x]) continue;
		dfs2(y,y);
	}	
}

int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	fd(i,18,0) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
	if(x==y) return x;
	fd(i,18,0) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}

void build(int x,int l,int r)
{
	tag[x]=inf;
	if(l==r){tr[x]=inf; return;}
	int mid=(l+r)>>1;
	build(L,l,mid),build(R,mid+1,r);
}

void down(int x)
{
	if(tag[x]==inf) return;
	tr[L]=min(tr[L],tag[x]),tag[L]=min(tag[L],tag[x]);
	tr[R]=min(tr[R],tag[x]),tag[R]=min(tag[R],tag[x]);
	tag[x]=inf;
}

void change(int x,int st,int en,int l,int r)
{
	if(st==l&&en==r)
	{
		tr[x]=min(tr[x],len),tag[x]=min(tag[x],len);
		return;
	}
	int mid=(st+en)>>1; down(x);
	if(r<=mid) change(L,st,mid,l,r);
	else if(l>mid) change(R,mid+1,en,l,r);
	else change(L,st,mid,l,mid),change(R,mid+1,en,mid+1,r);
}

void revise(int x,int l,int r,int p,int v)
{
	if(l==r) {tr[x]=v; return;}
	int mid=(l+r)>>1; down(x);
	if(p<=mid) revise(L,l,mid,p,v);
		else revise(R,mid+1,r,p,v);
}

int ask(int x,int l,int r,int p)
{
	if(l==r) return tr[x];
	int mid=(l+r)>>1; down(x);
	if(p<=mid) return ask(L,l,mid,p);
		else return ask(R,mid+1,r,p);
}

void find(int u,int v)
{
	int f1=top[u],f2=top[v];
	while(f1!=f2)
	{
		if(dep[f1]<dep[f2]) swap(f1,f2),swap(u,v);
		change(1,1,n,tid[f1],tid[u]);
		u=f[f1][0],f1=top[u];
	}
	if(dep[u]>dep[v]) swap(u,v);
	change(1,1,n,tid[u],tid[v]);
}

int main()
{
	freopen("worry.in","r",stdin);
	freopen("worry.out","w",stdout);
	scanf("%d%d",&n,&m);
	fo(i,1,n-1) scanf("%d%d",&a[i].x,&a[i].y),link(a[i].x,a[i].y),link(a[i].y,a[i].x);
	fo(i,1,n) size[i]=1,son[i]=-1;
	dfs1(1,0),dfs2(1,1),build(1,1,n);
	fo(j,1,18) fo(i,1,n) f[i][j]=f[f[i][j-1]][j-1];
	fo(i,1,m)
	{
		int x,y;
		scanf("%d%d%d",&x,&y,&len);
		int z=lca(x,y),lst=ask(1,1,n,tid[z]);
		find(x,z),find(y,z),revise(1,1,n,tid[z],lst);
	}
	fo(i,1,n-1)
	{
		if(dep[a[i].x]<dep[a[i].y]) swap(a[i].x,a[i].y);
		int ans=ask(1,1,n,tid[a[i].x]);
		printf("%d\n",ans==inf?-1:ans);
	}
}
Algorithm 2

copy from ZZY

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm> 
#define N 100010
using namespace std;
struct edge{int to,from,w;}e[N*2];
struct node{int x,y,v;}Q[N];
int n,m,cnt,fa[N],dep[N],k[N],d[N],p[N],head[N],father[N];
bool cmp(node a,node b) { return a.v<b.v; }
void insert(int x,int y,int z)
{ e[++cnt].to=y; e[cnt].from=head[x]; e[cnt].w=z; head[x]=cnt; }
int getfather(int x)
{ return father[x]?father[x]=getfather(father[x]):x; }
void work(int &x,int y)
{ d[p[x]]=y,father[x]=getfather(fa[x]),x=father[x]; }
void dfs(int x,int pre)
{
	fa[x]=pre,dep[x]=dep[pre]+1; 
	for (int i=head[x];i;i=e[i].from)
		if (e[i].to!=pre) p[e[i].to]=e[i].w,dfs(e[i].to,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;i++) scanf("%d%d",&x,&y),insert(x,y,i),insert(y,x,i),d[i]=-1;
	for (int i=1,x,y,z;i<=m;i++) scanf("%d%d%d",&Q[i].x,&Q[i].y,&Q[i].v);	
	dfs(1,0),sort(Q+1,Q+m+1,cmp);
	for (int i=1;i<=m;i++)
	{
		int x=getfather(Q[i].x),y=getfather(Q[i].y);
		while (x!=y) work(dep[x]<dep[y]?y:x,Q[i].v);
	}
	for (int i=1;i<n;i++) printf("%d\n",d[i]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值