【JZOJ5914】盟主的忧虑【LCA】【并查集】【BFS】

题目大意:

题目链接:https://jzoj.net/senior/#main/show/5914
题目图片:
http://wx4.sinaimg.cn/mw690/0060lm7Tly1fwqalfjtaej30j20d2gm3.jpg
http://wx3.sinaimg.cn/mw690/0060lm7Tly1fwqalfjtn0j30j50ec3yn.jpg
江湖由NN个门派组成,这些门派之间有N1N-1条小道将他们连接起来,每条道路都以“尺”为单位去计量,武林盟主发现任何两个门派都能够直接或者间接通过小道连接。
虽然整个江湖是可以互相到达的,但是他担心有心怀不轨之徒破坏这个武林的安定,破坏小道,于是武林盟主又秘密地修建了MM条密道,但每条小道距离都不超过1010亿尺。
果不其然,最近一个名叫“太吾”的组织意欲破坏武林的小道,请你帮盟主想想办法,如果门派AA到门派BB的直连小道被破坏,从AA走到BB的所有路径中,经过密道的距离最少是多少?

//蒟蒻语文不好不会概括求轻喷QWQ

思路 :

70分做法:

对于一棵树,我们切断它的其中一条边,那么这棵树一定会被切成两个互不相连的两棵树。而这切开的边连接的两个点肯定会分别在两棵新的数内。那么符合要求的新边就肯定是连接这两棵树的边中边权最小的边。
那么就先O(n)O(n)枚举每一条边,然后再O(n)O(n)并查集找到这两棵树,再枚举每条新边,如果这条边连接着两棵树,那么久用这条边的边权和答案取最小值。
时间复杂度:O(n2)O(n^2)
代码


100分做法:

先把新边按边权从小到大排序。
我们知道,如果在一棵树上增加一条新边,那么就会形成一个环。那么这个环上的所有没有答案的边的答案就是这条边。因为在这个环上去任意一条边(新边除外),环上的点还是可以互相到达。那么由于已经把新边按照边权排序了,所以可以直接将答案赋值上去。
然后用并查集表示有哪些边已经被合并了。这样到时候向上跳的时候就可以直接将合并过的点跳过。
那么就将新边连接的两个端点取LCALCA,分别往上跳,知道跳过LCALCA(可以视为深度比LCALCA低就停下)。然后跳到的边就赋值答案。
还有DFSDFS的时候会爆栈,所以就用BFSBFS代替即可。
时间复杂度:O(mlogn)O(mlogn)


代码(可读性1%1\%):

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define N 100100
#define LG 20
using namespace std;

int n,m,tot;
int father[N],head[N],ans[N],pre[N],f[N][LG+1],dep[N];

struct edge
{
	int next,to,num;
}e[N*2];

struct node
{
	int x,y,dis;
}a[N];

void add(int from,int to,int x)
{
	e[++tot].to=to;
	e[tot].num=x;
	e[tot].next=head[from];
	head[from]=tot;
}

void bfs()
{
	queue<int> X;
	queue<int> F;  //用队列代替栈
	X.push(1);
	F.push(0);
	while (X.size())
	{
		int x=X.front();
		int fa=F.front();
		dep[x]=dep[fa]+1;
		f[x][0]=fa;
		for (int i=1;i<=LG;i++)
		 f[x][i]=f[f[x][i-1]][i-1];  //求出LCA的f数组
		for (int i=head[x];~i;i=e[i].next)
		 if (e[i].to!=fa)
		 {
		 	pre[e[i].to]=e[i].num;
		 	X.push(e[i].to);
		 	F.push(x);
		 } 
		X.pop();
		F.pop();
	}
}

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

int find(int x)
{
	return father[x]==x?x:father[x]=find(father[x]);
}

bool cmp(node x,node y)
{
	return x.dis<y.dis;
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	int x,y,l;
	for (int i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y,i);
		add(y,x,i);
		ans[i]=-1;  //记录答案
	}
	for (int i=1;i<=m;i++)
	 scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].dis);
	bfs();
	sort(a+1,a+1+m,cmp);  //按权值排序
	for (int i=1;i<=n;i++)
	 father[i]=i;  //并查集初始化
	for (int i=1;i<=m;i++)
	{
		l=lca(a[i].x,a[i].y);
		for (x=find(a[i].x);dep[x]>dep[l];x=father[find(x)])  //往上跳
		{
			ans[pre[x]]=a[i].dis;  //记录答案
			father[find(x)]=find(f[find(x)][0]);  //更新集合
		}
		for (y=find(a[i].y);dep[y]>dep[l];y=father[find(y)])
		{
			ans[pre[y]]=a[i].dis;
			father[find(y)]=find(f[find(y)][0]);
		}
	}
	for (int i=1;i<n;i++)
	 printf("%d\n",ans[i]);
	return 0;
}
posted @ 2018-10-30 16:23  全OI最菜  阅读(107)  评论(0编辑  收藏  举报