时间限制: 1 Sec
内存限制: 128 MB
提交: 132
解决: 31
[提交][状态][讨论版]
题目描述
乱世天下,诸侯割据。每个诸侯王都有一片自己的领土。但是不是所有的诸侯王都是安分守己的,实力强大的诸侯国会设法吞并那些实力弱的,让自己的领土面积不断扩大。而实力弱的诸侯王为了不让自己的领土被吞并,他会联合一些其他同样弱小的诸侯国,组成联盟(联盟不止一个),来共同抵抗那些强大的诸侯国。 强大的诸侯国为了瓦解这些联盟,派出了最优秀的间谍来离间他们,使一些诸侯国退出联盟。最开始,每个诸侯国是一个联盟。
有两种操作
1、U x y 表示x和y在同一个联盟。
2、D x 表示x退出联盟。
输入
多组测试数据
第一行两个数,n和m(1 ≤ n≤ 10^5, 1 ≤ m ≤10^5),分别表示诸侯国的个数和操作次数。
接下来有m行操作
输出
输出联盟的个数
样例输入
10 1
U 0 9
5 7
U 0 1
U 1 2
U 0 3
D 0
U 1 4
D 2
U 0 2
样例输出
9
2
刚看到这道题的时候想着so easy,删除元素时候直接写了
else if(s=='D')
{
scanf("%d",&a);
per[a]=a;
}
根本没想到如果删除的是根节点的话整个集合就散了。。。后来研究了别人的,说是用虚根,研究了好久终于懂
就像别人说的送便当,便当从0之n-1编号,用w数组记录存放这些便当的盒子编号,当然最初编号为几的食物就放在几号盒子中,有些是要送给同一个人的,当然就把这些合并在一起,而对于某些要退食物的,就要把该食物编号从组织中删除,删除时候用w数组将该食物放在一个新盒子中,而原来放该食物的盒子不予以处理,以免打乱他们的关系。最后寻找这些食物各自的根节点,若该根节点记录过,跳过以免重复,没有记录过便ans+1,计算总组数的数目
#include<cstdio>
#include<cstring>
int n,m,per[12],w[100020],mark[12];//w需要定义的大些,它记录的是每个数据存放的真实位置
void init()//初始化
{
for(int i=0;i<n;i++)
{
per[i]=i;
w[i]=i;
}
}
int find(int x)//找根节点
{
int r=x;
while(r!=per[r])
r=per[r];
return r;
}
void join(int a,int b)//合并
{
int la=find(a);
int lb=find(b);
if(la!=lb)
per[la]=lb;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
int ans=0,a,b,t=n;//最初的虚节点记录为n
char s;
init();
while(m--)
{
getchar();//下面要输入字符,吸收回车键
scanf("%c",&s);//不许再用gtchar了,整型数据能够分辨
if(s=='U')
{
scanf("%d%d",&a,&b);
join(w[a],w[b]);//不可以写作a,b,需要合并其真实的位置
}
else if(s=='D')
{
scanf("%d",&a);
w[a]=t;//其真实位置,就像之前说的便当存放的真实盒子
per[t]=t;//父节点变为自己
t++;//下一个虚根
}
}
memset(mark,0,sizeof(mark));
for(int i=0;i<n;i++)
{
if(mark[find(w[i])]==0)//此根节点没有使用过
{
mark[find(w[i])]=1;//使用后记录一下
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}