题意就是模拟并查集进行合并操作,并且要求能删除
显然由于并查集用了路径压缩,树的结构已经改变了,并不支持真的删除..但是这里的删除仅仅是抹去该节点,将其独立出来,并不影响他所在的集合的别的点。那么我们只需要一个mark数组,记录 某个节点的真实下标,一开始mark[i]=i,当删除i之后,我们可以让他指向 n+i也就是,以后再访问i节点,都被映射到n+i这个节点,因为没有真正地删除i节点,所以不会影响并查集的结构。
此题的一神坑是 要 n||m退出,存在不合法数据
#include <cstdio> #include <cmath> #include <cstring> #include <string> #include <algorithm> #include <queue> #include <map> #include <set> #include <vector> #include <iostream> using namespace std; const double pi=acos(-1.0); double eps=0.000001; int fa[2000005+50]; int mark[2000005+50]; int vis[2000005]; int find(int x) { if (x==fa[x]) return x; else return fa[x]=find(fa[x]); } int max(int a,int b) {return a>b?a:b;} int main() { int i,t; int cun=1; int n,m; while( scanf("%d%d",&n,&m)&&(n||m)) { getchar(); memset(vis,0,sizeof(vis)); int cnt=n-1; int len=n+m; for (i=0;i<len;i++) fa[i]=i; for (i=0;i<len;i++) mark[i]=i; int x,y; char op; for (i=1;i<=m;i++) { scanf("%c",&op); if (op=='M') { scanf("%d%d",&x,&y); x=mark[x]; y=mark[y];//映射 int fx=find(x); int fy=find(y); if (fx!=fy) fa[fx]=fy; } else { scanf("%d",&x); mark[x]=++cnt; //“删除” } getchar(); } int ans=0; for (i=0;i<n;i++) { find(mark[i]); if (vis[fa[mark[i]]]==0) { vis[fa[mark[i]]]=1; ans++; } } printf("Case #%d: %d\n",cun++,ans); } return 0; }