1. 程式人生 > >hdu 2473(並查集刪除點)

hdu 2473(並查集刪除點)

algo 一起 open users ems scanf blog spa freopen

題目連接:

題意:給定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; }

hdu 2473(並查集刪除點)