zoukankan      html  css  js  c++  java
  • 【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
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    C语言 realloc为什么要有返回值,realloc返回值具体解释/(解决随意长度字符串输入问题)。
    opencv中的vs框架中的Blob Tracking Tests的中文注释。
    Java实现 蓝桥杯VIP 算法提高 棋盘多项式
    Java实现 蓝桥杯VIP 算法提高 棋盘多项式
    Java实现 蓝桥杯VIP 算法提高 棋盘多项式
    Java实现 蓝桥杯VIP 算法提高 棋盘多项式
    Java实现 蓝桥杯VIP 算法提高 分苹果
    Java实现 蓝桥杯VIP 算法提高 分苹果
    Java实现 蓝桥杯VIP 算法提高 分苹果
    Java实现 蓝桥杯VIP 算法提高 分苹果
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998477.html
Copyright © 2011-2022 走看看