HDU - 3038 How Many Answers Are Wrong 带权并查集

题目链接

HDU-3038

题意

给出一段区间1-n,下面m行给出一端连续区间a-b的和,如果遇到冲突就忽略掉,问共几条指令冲突

思路

带权并查集,我们在维护并查集的同时维护一个数组value,在这个题中,给出的a-b的和可以看成是a-1节点到b的距离。我们把value数组定义为到祖先节点的距离,当出现的a-1和b祖先不同时,我们连接他们,并且更新value数组,如果相同,则通过比对a-1和b的权值差与给定的和是否相等,不等就更新答案。
框架已经搭好,下面重点放在init,find,unite三个函数的设计上

  1. init(初始化)函数

    初始化是最简单的,我们除了要设置fa数组外,再初始化value数组为0即可

    int init(){
          
          
    	for(int i=0;i<maxn;i++){
          
          
    		fa[i]=i;
    		va[i]=0;
    	}
    }
    
  2. find(查找)函数

    普通的find函数就是递归加上路径压缩的优化。带权之后我们就需要更新value数组了。
    在更新的前面要先理解路径压缩,路径压缩就是在每次find后,把这次查找所有涉及到的所有节点都直接连接到祖先节点上。如图

    在这里插入图片描述

    在上图中,我们先链接12,再连接23,find函数查找1后,将1直接连到根节点3上了,当我们定义了value数组后,这张图是这样的

    在这里插入图片描述

    在连接好12后,我们的v1是1-2的距离,在连接好23后,v2是2-3的距离,而此时的v1还未更新,他并不是我们所定义的到根节点的距离,而只是到达他父节点的距离,所以在find函数中,我们应当更新v1,显然,1到根节点3的距离应为1的value加上父节点2的value,即v1+v2。具体到代码就是:

    int find(int x){
          
          
    	if(fa[x]==x)
    		return x;
    	int root=find(fa[x]);//祖先节点 
    	va[x]+=va[fa[x]];//更新一下va 
    	return fa[x]=root; //将x直接连接到祖先节点上 
    }
    
  3. unite(连接)函数

    这个是设计起来最麻烦的了,不考虑按轶合并的优化,普通的unite函数只需要把两个节点的祖先节点合并就可以了,那么加上value我们要怎么设计呢?

    在这里插入图片描述

    还是见图,我们连接1,3节点,那么首先找到根节点2,4.我们将2连接到4上,4的value按照定义为0,2本来的value是0,现在我们要更新它,让v2是2到4的距离。v1是1到2的距离,v3是3到4的距离,此外,题中还告诉了我们1到3的距离,那么1-2-4和1-3-4的路径长度应是相等的,也就是图中写的那个等式。代码实现如下

    void unite(int x,int y,int v){
          
          
    	int fx=find(x),fy=find(y);
    	fa[fx]=fy;
    	va[fx]=v+va[y]-va[x];
    } 
    

至此,我们的三个函数已经设计完成了。

代码

#include<bits/stdc++.h>
using namespace std;
	const int maxn=200005;
	int fa[maxn];
	int va[maxn];//距离祖先节点的距离 
	
	int n,m;
	
	int init(){
    
    
		for(int i=0;i<maxn;i++){
    
    
			fa[i]=i;
			va[i]=0;
		}
	}
	 
	int find(int x){
    
    
		if(fa[x]==x)
			return x;
		int root=find(fa[x]);//祖先节点 
		va[x]+=va[fa[x]];//更新一下va 
		return fa[x]=root; //将x直接连接到祖先节点上 
	}
	
	void unite(int x,int y,int v){
    
    
		int fx=find(x),fy=find(y);
		fa[fx]=fy;
		va[fx]=v+va[y]-va[x];
	} 
	
	int main(){
    
    
		int n,m,ans,x,y,z;
		while(cin>>n>>m){
    
    
			ans=0;
			init();
			while(m--){
    
    
				cin>>x>>y>>z;
				x--;
				if(find(x)==find(y)){
    
    
					if(va[x]-va[y]!=z) 
						ans++;
				}
				else{
    
    
					unite(x,y,z);
				} 
			}
			cout<<ans<<endl;
		}
	}	

猜你喜欢

转载自blog.csdn.net/TheSunspot/article/details/107768182