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都为异性,先说的是对的,如果后面给出关系与前面的 ...
随机推荐
- PHP的报错级别并返回当前级别error_reporting()
定义和用法:error_reporting() 设置 PHP 的报错级别并返回当前级别.函数语法:error_reporting(report_level) 如果参数 level 未指定,当前报错级别 ...
- jQuery下拉列表二级联动插件
jQuery下拉列表二级联动插件的视图代码: <!doctype html> <html lang="en"> <head> <meta ...
- 【题解】POI2014FAR-FarmCraft
这题首先手玩一下一下数据,写出每个节点修建软件所需要的时间和到达它的时间戳(第一次到达它的时间),不难发现实际上就是要最小化这两者之和.然后就想到:一棵子树内,时间戳必然是连续的一段区间,而如果将访问 ...
- BZOJ4008 [HNOI2015]亚瑟王 【概率dp】
题目链接 BZOJ4008 题解 要求所有牌造成伤害的期望,就是求每一张牌发动的概率\(g[i]\) 我们发现一张牌能否发动,还与其前面的牌是否发动有关 那我们设\(f[i][j]\)表示前\(i\) ...
- bzoj 5099 [POI2018]Pionek 计算几何 极角排序
[POI2018]Pionek Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 269 Solved: 80[Submit][Status][Disc ...
- synchronized ---- 作用
获得同步锁: 1.清空工作内存: 2.从主内存拷贝对象副本到工作内存: 3.执行代码(计算或者输出等): 4.刷新主内存数据: 5.释放同步锁.
- 【LuoguP1273有线电视网】树形依赖背包
参考论文http://wenku.baidu.com/view/8ab3daef5ef7ba0d4a733b25.html 参考一篇写的很好的博文http://www.cnblogs.com/GXZC ...
- 【Foreign】树 [prufer编码][DP]
树 Time Limit: 10 Sec Memory Limit: 256 MB Description Input Output Sample Input 3 2 2 1 Sample Outp ...
- codevs 3305 水果姐逛水果街Ⅱ&&codevs3006
题目描述 Description 水果姐第二天心情也很不错,又来逛水果街. 突然,cgh又出现了.cgh施展了魔法,水果街变成了树结构(店与店之间只有一条唯一的路径). 同样还是n家水果店,编号为1~ ...
- 【51NOD】1486 大大走格子
[算法]动态规划+组合数学 [题意]有一个h行w列的棋盘,定义一些格子为不能走的黑点,现在要求从左上角走到右下角的方案数. [题解] 大概能考虑到离散化黑点后,中间的空格子直接用组合数计算. 然后解决 ...