POJ 1703 Find them, Catch them【种类/带权并查集+判断两元素是否在同一集合/不同集合/无法确定+类似食物链】
Assume N (N <= 10^5) criminals are currently in Tadu City, numbered from 1 to N. And of course, at least one of them belongs to Gang Dragon, and the same for Gang Snake. You will be given M (M <= 10^5) messages in sequence, which are in the following two kinds:
1. D [a] [b]
where [a] and [b] are the numbers of two criminals, and they belong to different gangs.
2. A [a] [b]
where [a] and [b] are the numbers of two criminals. This requires you to decide whether a and b belong to a same gang.
Input
Output
Sample Input
- 1
- 5 5
- A 1 2
- D 1 2
- A 1 2
- D 2 4
- A 1 4
Sample Output
- Not sure yet.
- In different gangs.
- In the same gang.
- 【题意】:一共有俩犯罪团伙,N个人中有人可能是罪犯,D a b表示a和b属于同一个犯罪团伙,A a b表示询问a和b的团伙关系。(else:CSU-1904-精灵的交际网)
【分析】:
并查集的拓展:
并查集最开始的使用是用于判断一个图是否是连通图,由于并查集查询特点的特点(查询复杂度为O(1))所以用得很广。
并查集的变形一般是和向量偏移(类别偏移)一起结合 。
法一:- ***用了两个并查集,把每个罪犯复制出两个来。一个是原来的,一个对称后的,以判断a b的相对团伙。
同一个集合表示可以有一个罪犯所述团伙推出其他罪犯所属团伙,用于判断是否能确定。(数据不上50万都懒得用Rank按秩合并。- ***定义并查集为:并查集里的元素i-x表示i属于帮派x,同一个并查集的元素同时成立,可见所有元素个数为2 * N,如果i表示属于帮派A,那么i + N表示属于帮派B,
- 每次输入两个家伙不在同一帮派的时候,就合并他们分属两个帮派的元素。我认为这是最简单最好懂的算法,
- 那些利用复杂节点带权重接着依靠大量if-else维护并查集的做法都不够美。
- 这道题目其实归根结底就是保留了所有可能性,我们只知道x和y不属于同一集合,但我们不能确定究竟x属于集合A还是集合B,于是我们保留所有可能性,对x-A和x-B都做了一次记录。
- ***因为有两个帮派,因此对于每个人只要创建 2 个元素 i - A,i - B,并利用 2*N 个元素建立并查集。
- 假设 x , y属于不同的帮派,x , y + N 则是同一个帮派,x + N , y 同理。因此只需要将(x , y + N) 和 (x + N , y) 合并即可
- #include<cstdio>
- #include<cstring>
- using namespace std;
- #define N 200010
- int t,n,m,a,b,fa[N];
- int root(int x){
- return fa[x]==x?x:fa[x]=root(fa[x]);
- }
- inline bool alk(int x,int y){
- return root(x)==root(y);
- }
- inline void unite(int x,int y){
- x=root(x);
- y=root(y);
- if(x!=y) fa[x]=y;
- }
- int main(){
- scanf("%d",&t);
- char s[];
- while(t--){
- //memset(fa,0,sizeof(fa));
- scanf("%d%d%",&n,&m);
- for(int i=;i<=*n;i++)
- fa[i]=i;
- while(m--){
- scanf("%s%d%d",s,&a,&b);
- if(s[]=='D'){
- unite(a,b+n);
- unite(a+n,b);
- }
- else
- {
- if(root(a)==root(b))
- {
- printf("In the same gang.\n");
- }
- else if(root(a)==root(b+n))
- {
- printf("In different gangs.\n");
- }
- else
- {
- printf("Not sure yet.\n");
- }
- }
- }
- }
- }
- /*
- 375ms
- 988kB
- */
不用r[n]数组的种类并查集
【注意初始化2*n】
法二:(用scanf,cin要超时)因为ans的值只能为0和1(只有两个帮派),所以类别偏移可以用位运算.(http://www.cnblogs.com/zzy19961112/p/6043420.html)
带权并查集,利用r[ ]数组记录每个元素与其父亲节点的关系。 r[ x ] = 0 代表 x 与其父亲节点是同一个帮派的; r[ x ] = 1 代表 x 与其父亲节点是敌对帮派的; 一开始每个人都是自己的父亲节点 f[ x ] = x,每个人与自己的关系都是同属于一个阵营 r[ x ] = 0; 1、find( ) 函数寻找根节点的时候要不断更新 r[ ]数组 根据子节点与父节点的关系和父节点和爷爷节点的关系推到子节点和爷爷节点的关系。 很容易通过穷举发现其关系式:a 和 b 的关系为 r1, b 和 c 的关系为r2,则 a 和 c 的关系为: r3 = ( r1 + r2) % 2;
(爷爷,父亲) | (父亲,儿子) | (爷爷,儿子) |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
2、 Union的时候更新两棵树的关系
- 定义:fx 为 x的根节点, fy 为 y 的根节点,联合时,使得fa[ fy ] = fx;同时也要寻找 fx 和 fy 的关系,其关系为(r[ x ] + 1 - r[ y ]) % 2;
- 因为确定了 x 和 y 的关系是 1 ,因此 r[ fy ] = (r[ x ] + 1 - r[ y ]) % 2;
***********************************************************************************************http://blog.csdn.net/freezhanacmore/article/details/8774033
- Not sure yet.
- 如果find(x)等于 find(y) ,但是他们的r不等,说明属于不同帮派,输出In different gangs.
- 如果他们的r相等,说明属于同一个帮派,则输出In the same gang
- 注意:1.find()函数寻找根节点的时候要不断的更新 r
- 根据子节点与父亲节点的关系和父节点与爷爷节点的关系,推导子节点与爷爷节点的关系
- 如果 a 和 b 的关系是 r1, b 和 c 的关系是 r2,
- 那么 a 和 c 的关系就是 (r1+r2)%2 . PS:因为只用两种情况所以对 2 取模。
- 如果实在不好理解,那么我们就枚举推理一下,共有 2*2 = 4种情况:
- (a, b) (b, c) (a, c) (r1+r2)%2
- 0 0 0 0 a 和 b是同类 , b 和 c 是同类, 所以 a 和 c 也是同类
- 0 1 1 1 a 和 b是同类 , b 和 c 是异类, 所以 a 和 c 也是异类
- 1 0 1 1 a 和 b是异类 , b 和 c 是同类, 所以 a 和 c 是异类
- 1 1 0 0 a 和 b是异类 , b 和 c 是异类, 所以 a 和 c 是同类
- 2.Union()联合两棵树的时候也要更新两棵树的根的关系
- 定义:fx 为 x的根节点, fy 为 y 的根节点
- 联合时,使得 p[fx] = fy; 同时也要寻找 fx 与 fy 的关系。关系为:(r[x]+r[y]+1)%2
- 如何证明?
- fx 与 x 的关系是 r[x],
- x 与 y 的关系是 1 (因为确定是不同类,才联合的),
- y 与 fy 关系是 r[y],模 2 是因为只有两种关系
- 所以又上面的一点所推出的定理可以证明 fx 与 fy 的关系是: (r[x]+r[y]+1)%2
- #include <stdio.h>
- #define MAXV 100010
- int fa[MAXV],r[MAXV];
- int find(int x){
- int rt;
- if(r[x]!=x){
- rt=find(r[x]);
- fa[x]=fa[x]^fa[r[x]];//类别偏移
- return r[x]=rt;
- }
- return x;
- }
- void join(int x,int y){
- int fx,fy;
- fx=find(x);
- fy=find(y);
- r[fx]=fy;
- fa[fx]=~(fa[y]^fa[x]);//类别偏移
- }
- int main(){
- int i,n,m,a,b;
- char c;
- int t;
- scanf("%d",&t);
- while(t--){
- scanf("%d%d\n",&n,&m);
- for(i=;i<=n;i++){
- fa[i]=;
- r[i]=i;
- }
- for(i=;i<=m;i++){
- scanf("%c %d %d\n",&c,&a,&b);
- if(c=='D'){
- join(a,b);
- }else{
- if(n==) //特殊解
- printf("In different gangs.\n");
- else if(find(a)==find(b))
- {
- if(fa[a]==fa[b])
- printf("In the same gang.\n");
- else
- printf("In different gangs.\n");
- }
- else
- printf("Not sure yet.\n");
- }
- }
- }
- return ;
- }
- /*
- 344ms
- 988kB
- */
位运算版带权并查集
- #include<cstdio>
- const int maxn = +;
- int p[maxn]; //存父亲节点
- int r[maxn]; //存与根节点的关系,0 代表同类, 1代表不同类
- int find(int x) //找根节点
- {
- if(x == p[x]) return x;
- int t = p[x]; //记录父亲节点 方便下面更新r[]
- p[x] = find(p[x]);
- r[x] = (r[x]+r[t])%; //根据子节点与父亲节点的关系和父节点与爷爷节点的关系,推导子节点与爷爷节点的关系
- return p[x]; //容易忘记
- }
- void Union(int x, int y)
- {
- int fx = find(x); //x所在集合的根节点
- int fy = find(y);
- p[fx] = fy; //合并
- r[fx] = (r[x]++r[y])%; //fx与x关系 + x与y的关系 + y与fy的关系 = fx与fy的关系
- }
- void set(int n)
- {
- for(int x = ; x <= n; x++)
- {
- p[x] = x; //自己是自己的父节点
- r[x] = ; //自己和自己属于同一类
- }
- }
- int main()
- {
- int T;
- int n, m;
- scanf("%d", &T);
- while(T--)
- {
- scanf("%d%d%*c", &n, &m);
- set(n);
- char c;
- int x, y;
- while(m--)
- {
- scanf("%c%d%d%*c", &c, &x, &y); //注意输入
- //printf("%c\n", c);
- if(c == 'A')
- {
- if(find(x) == find(y)) //如果根节点相同,则表示能判断关系
- {
- if(r[x] != r[y]) printf("In different gangs.\n");
- else printf("In the same gang.\n");
- }
- else printf("Not sure yet.\n");
- }
- else if(c == 'D')
- {
- Union(x, y);
- }
- }
- }
- return ;
- }
取余版带权并查集
POJ 1703 Find them, Catch them【种类/带权并查集+判断两元素是否在同一集合/不同集合/无法确定+类似食物链】的更多相关文章
- POJ 1703 Find them, Catch them(带权并查集)
传送门 Find them, Catch them Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 42463 Accep ...
- (中等) POJ 1703 Find them, Catch them,带权并查集。
Description The police office in Tadu City decides to say ends to the chaos, as launch actions to ro ...
- poj 1703 - Find them, Catch them【带权并查集】
<题目链接> 题目大意: 已知所有元素要么属于第一个集合,要么属于第二个集合,给出两种操作.第一种是D a b,表示a,b两个元素不在一个集合里面.第二种操作是A a b,表示询问a,b两 ...
- 【poj 1988】Cube Stacking(图论--带权并查集)
题意:有N个方块,M个操作{"C x":查询方块x上的方块数:"M x y":移动方块x所在的整个方块堆到方块y所在的整个方块堆之上}.输出相应的答案. 解法: ...
- POJ 1984 Navigation Nightmare(二维带权并查集)
题目链接:http://poj.org/problem?id=1984 题目大意:有n个点,在平面上位于坐标点上,给出m关系F1 F2 L D ,表示点F1往D方向走L距离到点F2,然后给出一系 ...
- poj 2492 A Bug's Life【带权并查集】
就是给一个无向图判是否有奇环 用带权并查集来做,边权1表示连接的两个节点异性,否则同性,在%2意义下进行加法运算即可,最后判相同的时候也要%2,因为可能有负数 #include<iostream ...
- 【poj 1962】Corporative Network(图论--带权并查集 模版题)
P.S.我不想看英文原题的,但是看网上题解的题意看得我 炒鸡辛苦&一脸懵 +_+,打这模版题的代码也纠结至极了......不得已只能自己翻译了QwQ . 题意:有一个公司有N个企业,分成几个网 ...
- poj1703 Find them, Catch them(带权并查集)
题目链接 http://poj.org/problem?id=1703 题意 有两个帮派:龙帮和蛇帮,两个帮派共有n个人(编号1~n),输入m组数据,每组数据为D [a][b]或A [a][b],D[ ...
- POJ 2492 A Bug's Life(带权并查集)
题目链接:http://poj.org/problem?id=2492 题目大意:有n只虫子,m对关系,m行每行有x y两个编号的虫子,告诉你每对x和y都为异性,先说的是对的,如果后面给出关系与前面的 ...
随机推荐
- WebSocket简单介绍(WebSocket JavaScript 接口)(2)
上一节介绍了 WebSocket 规范,其中主要介绍了 WebSocket 的握手协议.握手协议通常是我们在构建 WebSocket 服务器端的实现和提供浏览器的WebSocket 支持时需要考虑的问 ...
- 洛谷P4591 [TJOI2018]碱基序列 【KMP + dp】
题目链接 洛谷P4591 题解 设\(f[i][j]\)表示前\(i\)个串匹配到位置\(j\)的方案数,匹配一下第\(i\)个串进行转移即可 本来写了\(hash\),发现没过,又写了一个\(KMP ...
- 【BZOJ 4565】 [Haoi2016]字符合并 区间dp+状压
考试的时候由于总是搞这道题导致爆零~~~~~(神™倒序难度.....) 考试的时候想着想着想用状压,但是觉得不行又想用区间dp,然而正解是状压着搞区间,这充分说明了一件事,状压不是只是一种dp而是一种 ...
- 使用setTimeout延时10ms执行onunloadcancel
在做Web开发时,我们经常用到页面关闭事件onbeforeunload,可以给用户一个选择放弃关闭的机会,就比如这个博客编辑器.如果用户选择了离开,那么onunload事件自然会触发:但若用户选择了取 ...
- NOIP2016愤怒的小鸟 [状压dp]
愤怒的小鸟 题目描述 Kiana 最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于 (0,0) 处,每次 Kiana 可以用它向第一象限发射一只红色的小鸟, ...
- POJ1308:Is It A Tree?(并查集)
Is It A Tree? 题目链接:http://poj.org/problem?id=1308 Description: A tree is a well-known data structure ...
- Nginx替换过滤文本模块replace-filter-nginx-module
1.安装此模块需要先安装sregex运行库 apt-get update;apt-get install git make gcc -y #Centos改成yum git clone https:// ...
- MyBatis的SQL语句映射文件详解(三)----多参数传递的几种方式
1.单一基本类型参数(String,int等) 单一的基本类型参数,将对应语句中的parameterType的值与参数的类型相同.然后直接 用“#{参数名}” 来获取 java代码 //String类 ...
- loj6087 毒瘤题
传送门:https://loj.ac/problem/6087 [题解] 这垃圾题目卡空间啊... k=1相信大家都会,把所有数异或起来就是答案了. 考虑k=2,把所有数异或起来得到两个答案数的异或值 ...
- appium===报错adb server version (31) doesn’t match this client (39); killing…的解决办法
当使用在cmd窗口调用adb shell命令的时候 提示如下: adb server version (31) doesn't match this client (39); killing...er ...