zoukankan      html  css  js  c++  java
  • Almost Union-Find(并查集删除操作)

    题意
    有n个人,有m个,三种操作
    ①.合并p所在的集合和q所在的集合
    ②.将p加入q所在的集合
    ③.询问p所在的集合中的元素个数以及元素之和

    一和三都是普通的并查集操作,主要是二,把一个单独的节点p加入q所在的集合,不太好想。
    由于并查集是一个树状结构,内部关系复杂,所以如果将其中的一个点直接拆出来,势必会导致树状结构解体
    用一个id[i]数组表示元素i的编号,每次的删除操作即给元素i分配一个新的编号。因为要求输出集合的元素个数和他们的和,所以我们需要维护这个信息,因为并查集的根结点在路径压缩中是不变的,所以将这两个信息维护在根结点上就行了

    #include <string.h>
    #include <stdio.h>
    #include <math.h>
    #include <algorithm>
    #define ll long long
    using namespace std;
    const int maxn=2e5+10;
    int id[maxn],pre[maxn];
    int sum[maxn],num[maxn];
    int n,m,cnt;
    int find(int u)
    {
    	if (pre[u]==u)
    		return u;
    	else
    	{
    		pre[u]=find(pre[u]);
    		return pre[u];
    	}
    }
    void merge(int u,int v)
    {
    	int t1=find(id[u]),t2=find(id[v]);
    	if (t1==t2)
    		return ;
    	pre[t1]=t2;
    	num[t2]+=num[t1];
    	sum[t2]+=sum[t1];
    }
    int main()
    {
    	int i,j,n,m,u,v,c;
    	while(~scanf("%d%d",&n,&m))
    	{
    		int t1,t2;
    		cnt=n;
    		for (i=1; i<=n; i++)
    		{
    			id[i]=pre[i]=sum[i]=i;
    			num[i]=1;
    		}
    		for (i=1; i<=m; i++)
    		{
    			scanf("%d",&c);
    			if (c==1)
    			{
    				scanf("%d%d",&u,&v);
    				merge(u,v);
    			}
    			else if(c==2)
    			{
    				scanf("%d%d",&u,&v);
    				int t1=find(id[u]),t2=find(id[v]);
    				if (t1!=t2)
    				{
    					num[t1]--;
    					sum[t1]-=u;//删除此节点
    					id[u]=++cnt;//u重新申请新节点
    					pre[id[u]]=id[u];
    					num[id[u]]=1;
    					sum[id[u]]=u;
    					merge(u,v);//u,id[u]代表的新节点初始化
    				}
    			}
    			else if (c==3)
    			{
    				scanf("%d",&u);
    				u=find(id[u]);
    				printf("%d %d
    ",num[u],sum[u]);
    			}
    		}
    	}
    	return 0;
    }
    /*
    并查集删除操作
    三种操作:
    1.合并p所在的集合和q所在的集合
    做法:p和q进行简单的并查集根节点合并即可,
    如果p,q两个的根节点一样,直接continue;
    
    2.将p节点加入q所在的集合
    2.1 在并查集中删除一个节点是很麻烦的事情,所以我们引入一个新的东西,
    用 id[i] 代替原有的 i 进行并查集操作,
    如果有一个点我们想把它从某一个集合中拿出来,
    就重新给 i 定义一个新的 id[i]=++cnt,这样就i不变,id变化。
    
    2.2 用一个数组id[]表示p所代表的代号,p当前的根为:
    pre[id[p]]
    当p被2操作时,我们只要改变它的id[p](比如 id[p]=++n),
    我们就可以让p有一个新的pre[]表示,而不用改变原来的根的表示,
    所以当后面的操作又用到p时,现在的 pre[id[p]] 已经不是之前那个了。
    
    3.询问p所在集合中的元素个数以及元素之和
    做法:开一个size数组和sum数组,在合并f数组时一起合并.
    
    */
  • 相关阅读:
    javascript高级程序设计---Event对象三
    javascript高级程序设计---Event对象二
    javascript高级程序设计---Event对象
    javascript高级程序设计---CSS操作
    javascript高级程序设计---Element对象
    javascript高级程序设计---document节点
    javascript高级程序设计---NodeList和HTMLCollection
    javascript高级程序设计---DOM
    Javascript高级程序设计——客户端检测
    学习javascript系列之变量
  • 原文地址:https://www.cnblogs.com/shidianshixuan/p/14351799.html
Copyright © 2011-2022 走看看