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;
    }
    
  • 相关阅读:
    eclipse中在线添加TestNG插件步骤(需联网)
    [IOI2008] Type Printer 打印机
    P2765 魔术球问题
    [HNOI2004]敲砖块
    P3931 SAC E#1
    [WC2005]友好的生物
    P1357 花园
    [SDOI2016]征途
    [APIO2014]序列分割
    [HNOI2008]玩具装箱TOY
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998476.html
Copyright © 2011-2022 走看看