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都为异性,先说的是对的,如果后面给出关系与前面的 ...
随机推荐
- [转载] Win7下MATLAB 7.0下载地址和详细安装
移步http://blog.csdn.net/feecooling/article/details/7525140 MATLAB中文手册命令汇总http://wenku.baidu.com/view/ ...
- 【题解】SDOI2018战略游戏
被CNST的大小卡了好久.一定要开到18呀…… 首先,遇到这种带各种各样环的图先考虑是不是可以建立圆方树,在圆方树上求出答案.然后转化为圆方树之后,我们就将图转化到了树上.答案非常的明显:只要一个圆点 ...
- 安卓topbar编码实战
1.先在res->value下新建attrs.xml文件 <?xml version="1.0" encoding="utf-8"?> < ...
- 修改centos的源
最近都在使用国内的VPS.系统统一使用的都是Linux系统.但是,有一些服务商的系统给默认设置的是国外的.这样就会导致下载速度缓慢.于是,找到了国内几家比较热门的镜像点.奉献给大家.下面的镜像全部支持 ...
- TCP ------ TCP创建服务器中出现的套接字
在服务器端,socket()返回的套接字用于监听(listen)和接受(accept)客户端的连接请求.这个套接字不能用于与客户端之间发送和接收数据. accept()接受一个客户端的连接请求,并返回 ...
- 在Maven中怎么配置外部Jar
转摘自:http://liugang594.iteye.com/blog/1677712 假设我们有一个Maven的project,其中有些Jar包不是来自Maven库的,是存在本地的某些Jar文件, ...
- im4java学习---阅读documentation文档
Utilities----im提供的一些工具类 ①.读取图片文件信息---Info类 我们之前的做法: op.format("width:%w,height:%h,path:%d%f,siz ...
- Fragment使用--文章集锦
android使用Fragment实现底部菜单使用show()和hide()来切换以保持Fragment状态 Android Fragment 真正的完全解析(上) Android Fragment实 ...
- java使用JNA调用dll
1.自己搞一个dll出来.参考下面链接 http://blog.csdn.net/lqena/article/details/46357165. 2.下载jar jna-4.2.1.jar. 3.复制 ...
- [洛谷P1074] 靶形数独
洛谷题目链接:靶形数独 题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博 ...