第十四届华中科技大学程序设计竞赛C题:Professional Manager(带删除的并查集)


题目链接 :点击打开链接


题目大意:

n个点,可以进行4种操作

1.将u,v所在的集合合并

2.将u从其所在集合删除

3.查询u所在集合的大小

4.查询u v是否在同一集合。


解题思路:

很明显的并查集模板,但是有个问题就是删除操作,而且这里面的删除操作不会导致原有的集合发生变化,意思就是只有这个点被删除掉。不考虑类似于图中边相连的情况。

专门学习了一下点的删除,其实就是用一个额外的数组记录i当前真正对应的位置,可以称为虚根吧,当我们吧u删除掉的时候,我们令pos[u]=++cnt,这样的话原来的u不会影响我们的其他操作,新的u也可以正常进行其他的操作。


Ac代码:

#include<bits/stdc++.h>
#define real re
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int INF=1e9+7;
const int mod=1e9+7;
  
int n,m,cnt,par[maxn*10],real[maxn],num[maxn*10];   //num记录集合的大小
void init() //初始化
{
    cnt=n;
    for(int i=0;i<=n;i++) par[i]=i;
    for(int i=0;i<=n;i++) real[i]=i;
    for(int i=0;i<=n;i++) num[i]=1;
}
int find(int x)
{
    if(par[x]==x) return x;
    else return par[x]=find(par[x]);
}
int main()
{
    int QAQ,kase=0;
    scanf("%d",&QAQ);
    while(QAQ--)
    {
        scanf("%d%d",&n,&m);
        init();
        printf("Case #%d:\n",++kase);
        while(m--)
        {
            int flag,u,v;
            scanf("%d",&flag);
            if(flag==1) //一切操作都以real执行
            {
                scanf("%d%d",&u,&v);
                int fx=find(real[u]),fy=find(real[v]);
                if(fx!=fy) par[fx]=fy,num[fy]+=num[fx];
            }
            if(flag==2) //这里注意num-- 然后开出新的点
            {
                scanf("%d",&u);
                num[find(real[u])]--;real[u]=++cnt;
                par[real[u]]=real[u];num[real[u]]=1;
            }
            if(flag==3)
            {
                scanf("%d",&u);
                printf("%d\n",num[find(real[u])]);
            }
            if(flag==4) //直接查询即可
            {
                scanf("%d%d",&u,&v);
                int fx=find(real[u]),fy=find(real[v]);
                if(fx==fy) printf("YES\n");
                else printf("NO\n");
            }
        }
    }
    //system("pause");
}



猜你喜欢

转载自blog.csdn.net/f2935552941/article/details/80157152