题目连接:
题意:给定n个点m次操作,有两种操作,M a b 操作是将a b合并到一起,S a 操作是从a所在的集合中删除a点,所有的操作结束后输出集合的个数。
题解:用并查集。删除集合中的点,建立虚父节点。
删除操作:
合并操作就是找到找到两个节点的父亲,修改父亲,如果删除就是将该点的父亲重新设置成自己,但是这是不行的,比如1,2,3的父亲都是1,现在删除1,1的父亲还是1,2,3也是1,集合还是1个,正确的应该是2个。
那删除节点的父亲不设成自己给新申请一个节点当做父亲,比如1,2,3的父亲都是1,在一个集合,现在删除1,申请了4当做1的父亲,2,3父亲都是1,然后Find(2)找2的父亲 2的父亲是1,但是1的父亲是4,所以给2的父亲更新成了4,3同理,所以还不行。
正确的方法是每一个点都设立一个虚拟父亲比如1,2,3的父亲分别是4,5,6,现在合并1,2,3都在一个集合,那他们的父亲都是4,现在删除1,那就给1重新申请一个节点7
现在2,3的父亲是4,1的父亲是7,删除成功。
代码:
#include <stdio.h> #include <algorithm> #include <math.h> #include <string.h> #include <vector> #include <queue> #include <map> #include <stack> #include <iostream> #define pi acos(-1.0) #define INF 0x3f3f3f3f using namespace std; #define ll long long const int maxn=5000010; int pre[maxn],id,vis[maxn]; int found(int x) { if(x!=pre[x]) pre[x]=found(pre[x]); return pre[x]; } void Merge(int a,int b) { int fx=found(a),fy=found(b); if(fx!=fy) pre[fx]=fy; } void del(int x) { pre[x]=id++; } int main() { freopen("C:\\Users\\Administrator\\Desktop\\a.txt","r",stdin); //ios::sync_with_stdio(false); //freopen("C:\\Users\\Administrator\\Desktop\\b.txt","w",stdout); int n,m,Case=0; while(scanf("%d%d",&n,&m),n+m) { for(int i=0;i<=n;i++) pre[i]=i+n; for(int i=n;i<=n+n+m;i++) pre[i]=i; id=n+n; int a,b; char ch[5]; for(int i=0;i<m;i++) { scanf("%s",ch); if(ch[0]==‘M‘) { scanf("%d%d",&a,&b); Merge(a,b); } else { scanf("%d",&a); del(a); } } int ans=0; memset(vis,0,sizeof vis); for(int i=0;i<n;i++) { int x=found(i); if(!vis[x]) ans++,vis[x]=1; } printf("Case #%d: %d\n",++Case,ans); } return 0; }
时间: 2024-11-02 05:09:59