Junk-Mail Filter HDU - 2473
考察:并查集
看了大佬的代码后才懂.本蒟蒻属实好菜..
正确思路:
考点就是并查集删除点的操作.如果每个点的父亲赋原值,那么findf函数后修改某一非叶子结点,后面的结点也会被修改为修改后的值.因此此法无效
正确做法是设置虚父结点.i的父亲是i+n,但初始化的时候和一般并查集有点不一样.在草稿纸上画图更容易理解,设置虚父结点后,1~n都变成了叶子结点,所以修改不会影响其他结点
易错:
如果写成++idx,初始化就要到<=n*2+m
2021.3.7 二刷,并查集直接的将p[x] = y,本质是将以x为根的树全部移到另一个根上,这里之所以设置虚父结点,本质利用并查集的自然的删除操作.
1 #include <iostream> 2 #include <algorithm> 3 #include <set> 4 using namespace std; 5 const int N = 1e5+10,M = 1e6+10; 6 int n,m,p[N*2+M],idx; 7 set<int> s; 8 int findf(int x) 9 { 10 if(x!=p[x]) p[x] = findf(p[x]); 11 return p[x]; 12 } 13 void del(int x) 14 { 15 p[x] = idx++; 16 } 17 void merge(int x,int y) 18 { 19 int px = findf(x),py = findf(y); 20 p[px] = py; 21 } 22 int main() 23 { 24 int kcase = 0; 25 while(scanf("%d%d",&n,&m)!=EOF&&n) 26 { 27 idx = n*2; int cnt =0; 28 s.clear(); 29 for(int i=0;i<n;i++) p[i] = i+n; 30 for(int i=n;i<n*2+m;i++) p[i] = i; 31 while(m--){ 32 char op[2]; 33 scanf("%s",op); 34 if(op[0]=='M') 35 { 36 int x,y; scanf("%d%d",&x,&y); 37 merge(x,y); 38 }else{ 39 int x; scanf("%d",&x); 40 del(x); 41 } 42 } 43 for(int i=0;i<n;i++) 44 { 45 int x = findf(i); 46 if(!s.count(x)) cnt++; 47 s.insert(x); 48 } 49 printf("Case #%d: %d\n",++kcase,cnt); 50 } 51 return 0; 52 }