这是一道神奇的题目,我调了大概一天多吧

首先hack一下翻译,操作3并没有要求查询后从其所在集合里删除该元素

于是我们来看一下这三个操作

第一个合并属于并查集的常规操作

第三个操作加权并查集也是可以解决的

至于第二个操作就是这个题的难点了

对于操作二的要求“ 将 \(p\) 移动至包含 \(q\) 的集合中,如果 \(p\) 和 \(q\) 已经在一个集合中,忽视此项操作”,我们可以发现这个操作其实隐含了一个删除的操作,即先从原集合中将 \(p\)删除,再将其移动到集合\(q\)中

我们知道并查集是并不支持删除操作的,于是我们必须对于这个毁天灭地的操作二想出一些奇技淫巧

于是呢,对于操作二,我们可以用一个虚点来代替那个要被从某个集合中删除的点,我们使这个虚点继承这个要被删除的点的所有状态,比如说元素和、元素个数和他的代表元素是谁,而我们在以后\(find\)里路径压缩时只要遇到以这个被删除的点为代表元素的元素,我们把他们的代表元素更新成这个虚点就好了

至于我们如何在路径压缩里判断这个点是否已经被从被某个虚点代替,这里就是真·奇技淫巧了

或许我们可以用map,但map的一次\(find\)复杂度可是\(O(log_2\ n)\)的,我们,可能只有我在这种多组数据的会t的非常欲仙欲死,于是我们的奇技淫巧就要登场了

它就是unordered_map

这是个什么东西呢,我们都知道map是用红黑树实现的,所以map内部\(key\)是有序的,但这也导致我们查询一个元素是否存在是要\(O(log_2\ n)\)的

而这个unordered_map,是c++ 11的新特性,是一个\(key\)无序的map,其内部是用哈希表实现的,单次查询的复杂度可以达到\(O(1)\)的

于是就是代码了

#include<iostream>
#include<map>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<tr1/unordered_map>
//unordered_map所需的头文件,同时在命名空间里加::tr1
#define re register
#define maxn 100001
#define gc getchar
using namespace std::tr1;
using namespace std;
unordered_map<int,int> s;
int fa[maxn<<2],tot[maxn<<2],sum[maxn<<2];
//因为我们虚点的是要用到n+1,n+2...的,所以多开几倍空间
int n,m;
inline int read()
{
char c=gc();
int dx=0;
while(c<'0'||c>'9') c=gc();
while(c>='0'&&c<='9')
dx=(dx<<3)+(dx<<1)+c-48,c=gc();
return dx;
}
int find(int x)
{
if(x==fa[x]) return fa[x];
if(s.count(fa[x])) fa[x]=s[fa[x]];
//如果一个元素的代表元素已经被删除了,那么我们就把它的代表元素换成其映射的那个虚点
return fa[x]=find(fa[x]);
}
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+48);
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)//多组数据标配
{
s.clear();
for(re int i=1;i<=n;i++)
fa[i]=sum[i]=i,tot[i]=1;
while(m--)
{
int p=read();
if(p==1)
{
int xx=find(read());
int yy=find(read());
if(xx==yy) continue;
tot[xx]+=tot[yy];
sum[xx]+=sum[yy];
fa[yy]=xx;
tot[yy]=sum[yy]=0;//常规合并操作
}
if(p==2)
{
int x=read();
int xx=find(x);
int yy=find(read());
if(xx==yy) continue;
if(fa[x]==x&&tot[x]==1)
{
tot[x]=0;
sum[x]=0;
fa[x]=yy;
tot[yy]+=1;
sum[yy]+=x;
continue;
}//如果这个元素代表元素是它自己,且元素个数为1,那它就不可能是其他元素的代表元素,于是我们直接fa[x]=yy就好了
if(s.find(x)==s.end())
{
s[x]=++n;
if(xx==x) fa[n]=n;
else fa[n]=xx;//如果这个元素代表元素是它自己,那么虚点继承这个状态的时候,让虚点的代表元素也是它自己就好了
tot[n]=tot[xx]-1;
sum[n]=sum[xx]-x;
tot[yy]+=1;
sum[yy]+=x;//向新集合添加x
tot[xx]-=1;
sum[xx]-=x;//从原集合中删除x
if(xx==x) sum[xx]=tot[xx]=0;
fa[x]=yy;
}else fa[x]=yy,sum[xx]-=x,tot[xx]-=1,tot[yy]+=1,sum[yy]+=x;
//如果这个元素已经被映射过了,即它已经被加入某一个集合,所以这个时候它不可能成为代表元素,于是这个时候我们也可以直接进行删除和合并的操作
}
if(p==3)
{
int xx=find(read());
write(tot[xx]),putchar(' '),write(sum[xx]);
putchar(10);
}
}
}
return 0;
}

UVA11987 【Almost Union-Find】的更多相关文章

  1. 【机器学习Machine Learning】资料大全

    昨天总结了深度学习的资料,今天把机器学习的资料也总结一下(友情提示:有些网站需要"科学上网"^_^) 推荐几本好书: 1.Pattern Recognition and Machi ...

  2. 一篇文章让Oracle程序猿学会MySql【未完待续】

    一篇文章让Oracle DB学会MySql[未完待续] 随笔前言: 本篇文章是针对已经能够熟练使用Oracle数据库的DB所写的快速学会MySql,为什么敢这么说,是因为本人认为Oracle在功能性方 ...

  3. 【转】【技术博客】Spark性能优化指南——高级篇

    http://mp.weixin.qq.com/s?__biz=MjM5NjQ5MTI5OA==&mid=2651745207&idx=1&sn=3d70d59cede236e ...

  4. 【windows核心编程】一个API拦截的例子

    API拦截 修改PE文件导入段中的导入函数地址 为 新的函数地址 这涉及PE文件格式中的导入表和IAT,PE文件中每个隐式链接的DLL对应一个IMAGE_IMPORT_DESCRIPTOR描述符结构, ...

  5. 【百度地图API】多家地图API内存消耗对比测验(带源码)

    原文:[百度地图API]多家地图API内存消耗对比测验(带源码) 任务描述: 啊,美妙的春节结束了.酸奶小妹和妈妈的山西平遥之旅也宣告成功!距离平遥古城7km,有一个同样身为“世界文化遗产”的寺庙,叫 ...

  6. 【Spark调优】大表join大表,少数key导致数据倾斜解决方案

    [使用场景] 两个RDD进行join的时候,如果数据量都比较大,那么此时可以sample看下两个RDD中的key分布情况.如果出现数据倾斜,是因为其中某一个RDD中的少数几个key的数据量过大,而另一 ...

  7. (转)【面试】【MySQL常见问题总结】【03】

    [常见面试问题总结目录>>>] [面试][MySQL常见问题总结][03] 2016-05-29 22:20 阅读(8244) 评论(2) [面试][MySQL常见问题总结][02] ...

  8. 【Python之路】第二十篇--MySQL(二)

    视图 视图是一个虚拟表(非真实存在),其本质是[根据SQL语句获取动态的数据集,并为其命名], 用户使用时只需使用[名称]即可获取结果集,并可以将其当作表来使用. 1.创建视图 --格式:CREATE ...

  9. 【vim环境配置】详细实录

    [写在前面] 以下的所有内容主要参照: https://github.com/yangyangwithgnu/use_vim_as_ide . 原blog作者写的非常用心,建议大家都去看看.(个人觉得 ...

  10. 【并查集】关押罪犯(BSOJ2809)

    Description S城现有两座监狱,一共关押着N名罪犯,编号分别为1~N.他们之间的关系自然也极不和谐.很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突.我们用“怨 气值”(一个正整 ...

随机推荐

  1. [H5表单]一些html5表单知识及EventUtil对象完善

    紧接着上面的文章,一开始准备一篇文章搞定,后来看到,要总结的东西还不少,干脆,把上面文章拆成两部分吧,这部分主要讲讲表单知识! 表单知识 1.Html5的autofocus属性. 有个这个属性,我们不 ...

  2. Java - 用builder代替构造器

    静态工厂和够构造器有一个共同的局限性:遇到大量的参数时无法很好的扩展. 先说说构造器. 其实field不多时重叠构造器(telescoping constructor)是个不错的方法,易于编写也易于调 ...

  3. [javaEE] 三层架构案例-用户模块(一)

    用户注册登录注销 Servlet+JSP+javaBean+dom4j 分层结构: com.tsh.web com.tsh.service com.tsh.dao com.tsh.domain com ...

  4. HTML DOM status 属性

    <!DOCTYPE html><html> <head>HTML DOM status 属性</head><body><script ...

  5. 流畅的python和cookbook学习笔记(三)

    1.双向队列 collections.deque 类(双向队列)是一个线程安全.可以快速从两端添加或者删除元素的数据类型. rotate和popleft操作,rorate可以把前后元素换位.pople ...

  6. spring和jdbc结合的一个小例子

    1.新建一个SpringJdbc的maven项目. 2.引入需要的jar包的依赖 <span style="white-space:pre"> </span> ...

  7. pom文件解析

    Maven的依赖是使用Maven坐标来定位的,而Maven坐标主要由GAV(groupId, artifactId, version)构成.因此,使用任何一个依赖之间,你都需要知道它的Maven坐标. ...

  8. csharp: Converting chinese character to Unicode

    Function chinese2unicode(Str) Dim Str_one:Str_one = "" Dim Str_unicode:Str_unicode = " ...

  9. 排序算法Nb三人组-快速排序

    核心思想: 将列表中第一个元素拿出来,放到一边,左右两个循环,左面的大于拿出来的数,就把他挪到右面, 右面的小于拿出来的数就把他放在左面,这是列表被第一个元素''分''为两个列表,在对两个列表进行同样 ...

  10. 排序算法lowb三人组-插入排序

    def insert_sort(li): for i in range(1, len(li)): # i表示摸到的牌的下标 tmp = li[i] # 摸到的牌 j = i - 1 while j & ...