1022 合纵连横【并查集 元素的删除】

合纵连横

时间限制:1000 ms  |  内存限制:65535 KB

难度:3

描述

乱世天下,诸侯割据。每个诸侯王都有一片自己的领土。但是不是所有的诸侯王都是安分守己的,实力强大的诸侯国会设法吞并那些实力弱的,让自己的领土面积不断扩大。而实力弱的诸侯王为了不让自己的领土被吞并,他会联合一些其他同样弱小的诸侯国,组成联盟(联盟不止一个),来共同抵抗那些强大的诸侯国。 强大的诸侯国为了瓦解这些联盟,派出了最优秀的间谍来离间他们,使一些诸侯国退出联盟。最开始,每个诸侯国是一个联盟。

有两种操作

1、U x y 表示x和y在同一个联盟。(0≤x,y<n)

2、D x   表示x退出联盟。

输入
多组测试数据

第一行两个数,n和m(1 ≤ n≤ 10^5, 1 ≤ m ≤10^5),分别表示诸侯国的个数和操作次数。

接下来有m行操作

输出
输出联盟的个数
样例输入
5 7
U 0 1
U 1 2
U 0 3
D 0
U 1 4
D 2
U 0 2
10 1
U 0 9
样例输出
Case #1: 2
Case #2: 9

刚看到这个题的时候,很明显的是并查集的知识,然后就开敲代码,然后写着写着发现不对了........

同以往的不一样的是,平常的题只是往里面加元素,判断连通或者集合的个数之类的,但是这个题,需要删去某个元素,当然,如果这个元素不是父节点,直接让这个元素直接保存为自己的编号值就行(自己是自己的父节点),但是如果自身是父节点的时候,直接这样操作就会把这个集合打乱,后边的操作就肯定不对了,然后想了想,没解决办法,毕竟数据和现实不一样,并查集里一个集合里的元素,是由一个父节点联系起来的,而不是所有的元素之间都有联系,可以随便删除,然后上网找并查集删除的相关内容,不过都不够详细,然后自己靠自己的理解说其中一种方法,方法不在多,关键在自己理解。

有个方法是用个辅助集合标记来做的,自己也不是完全理解,但是好像懂了点,用我代码里面的数据来说说自己的理解,一个per 数组,和往常一样,保存哪几个数据之间有关系,一个辅助 v 数组保存的值和它每个下标都相同,相当于现在某个元素,都有了两个,那么操作的时候,合并和查找的时候,对数组 v 的元素或者下标进行对应的并查集的操作可以实现相同的功能,然后需要执行删除操作的时候,就开始“删除”操作,这里的删除其实要做的并不是把某个元素真正从集合里面删除掉了,他和别的元素的关系还在,但是这个元素的真正位置已经移到了
per 数组的其他区域(不会与别的元素区域重合),而且呢,v 数组相应的坐标的元素里面储存着这个元素的位置,不至于最后找不到它是第几号元素,等到下一次操作如果需要拿这个元素和别的元素合并的话,就是相当于先从 v 数组里面找到这个元素现在放的位置,然后在per 数组里对这个位置的元素进行合并和查找等操作,所有的操作都这样做,也就相当于这个元素虽然在per 数组里出现了好几次,但是它真正的位置在 v 数组里面,这样的方法也就相当于间接进行 per 数组里面元素的操作了,但是在操作的时候和一般的方法一样,  某个元素在per数组里面的真正有效位置(也就是操作的下标)保存在
v 数组里它对应的下标中,一般的并查集操作则是per数组的下标就是对应元素自身,算是直接访问了当前元素,而这样用辅助数组的方法,算是间接的访问某个元素的位置。

遍历有几个集合,那么直接就访问 v 数组,每次都取出v 数组里面保存的当前这个元素在 per 数组里真正的位置,然后找到他的根节点,统计个数,并且直接标记上,只有下一次根节点不一样的时候,才更新统计数量,最后就可以得到具体有多少集合了...

ps:自己理解的不深,也在想怎么用更好的方法来统计不同的集合的数量,个人意见,不足之处还请大神指教

#include<stdio.h>
#include<string.h>
int per[100005],v[100005],mark[100005],n;// 1 万个数据,来回运算,需要很大的空间,而且这个方法的确很浪费空间...
void init()//初始化
{
	for(int i=0;i<n;++i)
	{
		per[i]=i;
		v[i]=i;
	}
}
int find(int x)//查找
{
	int r=x;
	while(r!=per[r])
	{
		r=per[r];
	}
	int i=x,j;
	while(i!=r)//经典的路径压缩
	{
		j=per[i];per[i]=r;i=j;
	}
	return r;
}
void join(int x,int y)//合并
{
	int fx=find(x),fy=find(y);
	if(fx!=fy)
	{
		per[fy]=fx;
	}
}
int main()
{
	int t=0,i,m,a,b,k;char s;
	while(~scanf("%d%d",&n,&m))
	{
		init();k=n;//现在k指的位置处空白
		memset(mark,0,sizeof(mark));
		for(i=0;i<m;++i)
		{
			scanf(" %c",&s);
			if(s=='U')
			{
				scanf("%d%d",&a,&b);
				join(v[a],v[b]);
			}
			else
			{
				scanf("%d",&a);
				v[a]=k;//标记转移到的位置,
				per[k]=k;//把这个位置处保存上他的操作编号(下标)
				++k;//k指向下一个空白处
			}
		}
		int cnt=0;
		for(i=0;i<n;++i)
		{
			int temp=find(v[i]);//找根节点
			if(!mark[temp])//如果根节点没出现过
			{
				mark[temp]=1;//标记
				++cnt;//累加
			}
		}
		printf("Case #%d: %d\n",++t,cnt);//按格式输出
	}
	return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-19 20:32:46

1022 合纵连横【并查集 元素的删除】的相关文章

hdoj 2473 Junk-Mail Filter【并查集节点的删除】

Junk-Mail Filter Time Limit: 15000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 7515    Accepted Submission(s): 2368 Problem Description Recognizing junk mails is a tough task. The method used here consists o

HDU 2473 Junk-Mail Filter 并查集,虚拟删除操作

给定两种操作 第一种是合并X Y 第二种是把X分离出来,就是从原来的集合中分离出来,其它的关系不变. 关键是怎么分离,可以考虑把它变成一个其它值.HASH[i] = other_val 然后用新值去做并查集即可. 需要注意的一点就是 加入现在根是1,fa[1] = 1, fa[2] = 1, fa[3] = 1 那么如果你删除了1,这应该输出2.但是现在是fa[2] = 1,是一个不存在的根了,这个时候ans应该+1,但是按照并查集的思路 if (find(HASH[i]) == HASH[i]

hdu 2473 Junk-Mail Filter 并查集删除

点击打开链接 http://acm.hdu.edu.cn/showproblem.php?pid=2473 题意:给出n种操作,M a b表示a和b是同一个并查集,S a表示在并查集中删除a,要注意的是,如果1和2是一个并查集,2和3是一个并查集,那么1和3也是一个并查集,即使删除2之后,我们依然可以得到1和3还是一个集合里的. 思路:由于并查集是一种树结构,无法在树种删除一个点后还让树继续保持着之前的样子,所以我们删除是采取的操作是使用映射,一开始所有点的映射都是自己,然后删除操作就是把这个点

【BZOJ-3673&amp;3674】可持久化并查集 可持久化线段树 + 并查集

3673: 可持久化并查集 by zky Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 1878  Solved: 846[Submit][Status][Discuss] Description n个集合 m个操作操作:1 a b 合并a,b所在集合2 k 回到第k次操作之后的状态(查询算作操作)3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 0<n,m<=2*10^4 Input Output Sample Input 5 6

图论(一):DFS,BFS,邻接链表,并查集

本文总结了图的深度优先搜索,图的广度优先搜索,邻接链表和邻接矩阵的实现,并查集的实现. 0),预备知识 基础词汇:有向图,无向图,带权有向图,带权无向图,有向图中<Vi, Vj>:即Vi--->Vj,弧尾--->弧头,无向图中相邻记为(Vi, Vj),顶点有穷集合V+边的有穷集合E. 图的两种实现方式:1,邻接矩阵:edge[n][n]表示有n个结点,数组内容为权值大小或者是否存在边(∞表示无边,权值或1表示有边,0表示结点到结点本身): 2,邻接链表:针对稀疏矩阵较适宜,为图的每

BZOJ1015[JSOI2008]星球大战starwar[并查集]

1015: [JSOI2008]星球大战starwar Time Limit: 3 Sec  Memory Limit: 162 MBSubmit: 5253  Solved: 2395[Submit][Status][Discuss] Description 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这些星球通过特殊的以太隧道互相直接或间接地连接. 但好景不长,很快帝国又重

数据结构 之 并查集(Disjoint Set)

一.并查集的概念: 首先,为了引出并查集,先介绍几个概念: 1.等价关系(Equivalent Relation) 自反性.对称性.传递性. 如果a和b存在等价关系,记为a~b. 2.等价类: 一个元素a(a属于S)的等价类是S的一个子集,它包含所有与a有关系的元素.注意,等价类形成对S的一个划分:S的每一个成员恰好互斥地出现在一个等价类中.为了确定是否a~b,我们仅需验证a和b是否属于同一个等价类即可. 3.并查集: 即为等价类,同一等价类(并查集)中元素两两存在等价关系,不同并查集元素之间没

并查集:POJ 1182 食物链 复习

#include <iostream> #include <algorithm> #include <cstring> #include <cstdlib> #include <cstdio> using namespace std; const int maxn = 100000*3 + 100; int par[maxn]; int Rank[maxn]; int N, K; int T[maxn], X[maxn], Y[maxn]; //

NYOJ合纵连横【并查集的合并和删除】

合纵连横 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述 乱世天下,诸侯割据.每个诸侯王都有一片自己的领土.但是不是所有的诸侯王都是安分守己的,实力强大的诸侯国会设法吞并那些实力弱的,让自己的领土面积不断扩大.而实力弱的诸侯王为了不让自己的领土被吞并,他会联合一些其他同样弱小的诸侯国,组成联盟(联盟不止一个),来共同抵抗那些强大的诸侯国. 强大的诸侯国为了瓦解这些联盟,派出了最优秀的间谍来离间他们,使一些诸侯国退出联盟.最开始,每个诸侯国是一个联盟. 有两种操作 1