题解 2018寒假训练1 H.垃圾邮件过滤器(虚拟结点)

zhouwc

2018-02-25 09:45:45

Personal

问题 H: 垃圾邮件过滤器 时间限制: 1 Sec 内存限制: 128 MB [提交][状态][讨论版] 题目描述 要处理垃圾邮件,就要对邮件归类。现在有n封邮件,邮件编号0~n-1,m个操作: 1. M X Y 表示邮件X和Y是同一类 2. S X 表示邮件X是特殊类,这个时候,他就要独立出来单独归类 比如有3封邮件,3个操作: M 0 1, M 1 2, S 1 前两个操作表示0 1 2封邮件都属于一类,经过第三个操作后,发现1是特殊类,但0 2还是属于同一类,于是总共有两类邮件。 现在问题就是给你这些邮件和操作,问总共有几类邮件? 输入 输入n m 输入m个操作,如描述格式 输出 输出有几类邮件 样例输入 3 3 M 0 1 M 1 2 S 1 样例输出 2 提示 【样例说明】 其他样例: 输入: 5 6 M 0 1 M 1 2 M 1 3 S 1 M 1 2 S 3 输出: 3 解释: 前三个操作把0 1 2 3归为一类, 然后发现1要单独归类,于是把1与0 2 3分开 后来发现1和 2是同一类,于是继续把1归到2的分类中 最后发现3是单独一类 最终0 1 2是一类,3一类,4一类 总共3类 【数据规模和约定】 1<=N<=10^5 1<=M<=10^6 题解 没出现两个结点合并,出现1个结点删除其原来的关系,但原来的关系网不会因为它而改变 比如M 0 1, M 1 2, S 1,删除了1,原来因为1才分为一类的0 2还是归为一类 所以问题需要本来连接0 2的1还存在,这里就引进虚结点 0-1-2,如果要删除1,改成将1从新定位到新结点(从n开始编号),变为 0-1(虚拟)-2 3(为原来的1) 这样的话,还是分为了2类,第一类0 1 2,只不过1是虚拟结点,第二类是3,这个3本来是1 所以对于每个结点,设一个id[i],表示它将映射成新结点 初始的时候,id[i]=i, 需要改变的时候id[i]=tot++; 剩下的做法,还是基本的并查集。 ```cpp #include<cstdio> #include<cstring> using namespace std; int fg[110005],fa[110005],id[1100005]; int n,m,a,b,tot,root,ans; char c; int find(int x){ if(fa[x]!=x) fa[x]=find(fa[x]); return fa[x]; } int main(){ scanf("%d%d",&n,&m); tot=n; for(int i=0;i<n+m;i++){ fa[i]=i; id[i]=i; } for(int i=0;i<m;i++) { scanf(" %c",&c); if(c=='S') { scanf("%d",&a); id[a]=tot++; }else { scanf("%d%d",&a,&b); fa[find(id[b])]=find(id[a]); } } for(int i=0;i<n;i++) fg[find(id[i])]=1; for(int i=0;i<tot;i++) { if(fg[i])ans++; } printf("%d",ans); return 0; } ```