hdu 2473 Junk-Mail Filter(并查集_虚节点)

感觉有些难的题,刚开始就想到了设立虚节点,但是实现总是出错,因为每次设立了虚节点之后,无法将原节点和虚节点分开,导致虚节点根本无意义。

以上纯属废话,可以忽略……

题意——

给定n个点(0, 1, 2, ..., n-1),可进行两种操作:1. 将两个点合并到一个集合中; 2. 将一个点从原有集合中取出。问最后点有几个集合。

很明显的并查集,包含合并,删除操作。

但是,删除某节点的时候,需要保证这个集合中,除了被删除节点的其它节点不变,这点有些难以处理。

我们知道,并查集其实是一棵棵树,我们将树中某节点删除,还要保证树的结构不变,可以采用一种方法,那就是“虚节点”,也就是说,我们并不删除那个节点,却将需要删除的节点中保存的数据转移到一个新的节点中,这个节点独立于这棵树之外。这样一来,我们查询那个节点的时候,会查询到新的节点,而原本的节点的作用仅仅是保持树的结构。

因此,我们需要两个数组,一个数组是fm[N+M],一个数组是fle[N],其中fle[]数组的fle[i]表示第i个节点的值,而fm[]数组中,fm[fle[i]]用来保存第i个节点的前驱,即fle[i]的父节点。

重点就在于这个fle[],每次删除节点i时,我们不改变fm[fle[i]],这样树的结构就不会变,而我们赋予fle[i]一个从未使用过的新值,就使实际上的i节点变化了。

这样每次删除i节点,都只需要赋予fle[i]一个从未用过的新值,类似于给i节点换一个新马甲,就可以解决问题。

我们可以从值为n开始,给节点赋的值依次为n++,这样,因为最多M次操作,那么最多换M个马甲,因此,fm的大小为M+N。

上代码——

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cmath>
 4 #include <algorithm>
 5 using namespace std;
 6
 7 const int N = 100010;
 8 const int M = 1000010;
 9
10 bool vis[N+M];
11 int fm[N+M], fle[N];
12 int n, m, a, b, pt, ans, tm;
13 char s[10];
14
15 void init()
16 {
17     for(int i = 0; i < n; i++)
18     {
19         fm[i] = i;
20         fle[i] = i;
21     }
22     memset(vis, 0, sizeof(vis));
23     pt = n;
24     ans = 0;
25 }
26
27 int mfind(int a)
28 {
29     int fa = a;
30
31     while(fa != fm[fa]) fa = fm[fa];
32     while(a != fm[a])
33     {
34         int mid = fm[a];
35         fm[a] = fa;
36         a = mid;
37     }
38     return fa;
39 }
40
41 void mmerge()
42 {
43     int fa = mfind(fm[fle[a]]);
44     int fb = mfind(fm[fle[b]]);
45     if(fa != fb) fm[fa] = fb;
46 }
47
48 void dmerge()
49 {
50     fle[a] = pt;        //换马甲
51     fm[pt] = pt;        //给这个新值初始化父节点
52     pt++;               //为新马甲做准备
53 }
54
55 void work()
56 {
57     while(m--)
58     {
59         scanf("%s", s);
60         if(s[0] == ‘M‘)
61         {
62             scanf("%d%d", &a, &b);
63             mmerge();
64         }
65         else if(s[0] == ‘S‘)
66         {
67             scanf("%d", &a);
68             dmerge();
69         }
70     }
71
72     for(int i = 0; i < n; i++)
73     {
74         int ance = mfind(fle[i]);
75         if(!vis[ance])
76         {
77             ans++;
78             vis[ance] = 1;
79         }
80     }
81     printf("Case #%d: %d\n", tm++, ans);
82 }
83
84
85
86 int main()
87 {
88     //freopen("test.in", "r", stdin);
89     tm = 1;
90     while(~scanf("%d%d", &n, &m) && (n+m))
91     {
92         init();
93         work();
94     }
95 }

ps: 这几天状态一直不好,但是今天突然看见一句话,感由心生,终于耐下心来弄明白了这道题——心若没有栖息的地方,在哪里都是流浪

继续加油吧……

时间: 2024-10-06 00:17:25

hdu 2473 Junk-Mail Filter(并查集_虚节点)的相关文章

HDU 3635 延缓更新的并查集

Dragon Balls Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2839    Accepted Submission(s): 1097 Problem Description Five hundred years later, the number of dragon balls will increase unexpecte

HDU 3461 Code Lock(并查集的应用+快速幂)

* 65536kb,只能开到1.76*10^7大小的数组.而题目的N取到了10^7,我开始做的时候没注意,用了按秩合并,uset+rank达到了2*10^7所以MLE,所以貌似不能用按秩合并. 其实路径压缩也可以不用.............  题目的大意: 一个密码锁上有编号为1到N的N个字母,每个字母可以取26个小写英文字母中的一个.再给你M个区间[L,M],表示该区间的字母可以一起同步"增加"(从'a'变为'b'为增1,'z'增1为'a').假如一组密码按照给定的区间进行有限

(hdu step 5.1.5)Dragon Balls(求并查集的根节点、节点数和个结点的移动次数)

题目: Dragon Balls Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 562 Accepted Submission(s): 239   Problem Description Five hundred years later, the number of dragon balls will increase unexpected

HDU 1558 Segment set (并查集+线段非规范相交)

题目链接 题意 : 如果两个线段相交就属于同一集合,查询某条线段所属集合有多少线段,输出. 思路 : 先判断与其他线段是否相交,然后合并. 1 //1558 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <cmath> 6 #define eps 1e-8 7 #define zero(x) (((x) > 0 ? (x) : (-x)) < e

并查集间单个节点的转移(UVa 11987 Almost Union-Find)

从来没有这么艰难地完成一道算法题过!经过8次失败之后总算提交成功了!所以决定写一篇博文,对并查集的相关内容做一些总结. 普通并查集的操作无非是两种,find_set(x)即找到节点x所在的集合的代表节点,或者是union_set(x,y),即将x和y所在的两个集合合并起来.如下图所示,有左右两个并集 通常,我们会选用并查集中父节点为自己的元素作为这个并查集的代表,例如图中的节点a和节点e.那么,我们如何通过集合中的一个节点找到该节点所在集合的代表节点呢?其实很简单,例如上图中的d节点,它首先通过

HDU 2473 Junk-Mail Filter (并查集的删除操作)

Problem Description Recognizing junk mails is a tough task. The method used here consists of two steps: 1) Extract the common characteristics from the incoming email. 2) Use a filter matching the set of common characteristics extracted to determine w

hdu 2473 Junk-Mail Filter 并查集删除

点击打开链接 http://acm.hdu.edu.cn/showproblem.php?pid=2473 题意:给出n种操作,M a b表示a和b是同一个并查集,S a表示在并查集中删除a,要注意的是,如果1和2是一个并查集,2和3是一个并查集,那么1和3也是一个并查集,即使删除2之后,我们依然可以得到1和3还是一个集合里的. 思路:由于并查集是一种树结构,无法在树种删除一个点后还让树继续保持着之前的样子,所以我们删除是采取的操作是使用映射,一开始所有点的映射都是自己,然后删除操作就是把这个点

HDU 2473 Junk-Mail Filter (并查集节点删除)

Junk-Mail Filter Time Limit: 15000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 7254    Accepted Submission(s): 2307 Problem Description Recognizing junk mails is a tough task. The method used here consists o

hdu 2473 Junk-Mail Filter (并查集之点的删除)

Junk-Mail Filter Time Limit: 15000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 6276    Accepted Submission(s): 1981 Problem Description Recognizing junk mails is a tough task. The method used here consists o