poj 1182 食物链 带权并查集

来源:互联网 发布:nba2013 14总决赛数据 编辑:程序博客网 时间:2024/05/01 23:30

神奇&坑的一题。中文题题意不解释,注意这里输入只有一组数据,EOF会报WA。详解请参考http://blog.csdn.net/c0de4fun/article/details/7318642

这里看两点:1,处理路径压缩时候的关系。2,集合合并时候的关系,当然1 是 2 的前提

1.首先定义节点之间的关系(这里的关系就是a == b, a 吃 b, b 吃 a ,a 是 b 父亲),那么定义x = a - b; 有前面得x = 0表示a,b同属一物种,x = 1表示a 吃 b ,x = 2表示 b 吃 a

那么显然可以得到下面两点:

  1. ani [ now ].parent = ani [ ani [ now ].parent ].parent;     //即当前儿子的祖先节点是其父亲的祖先节点
  2. ani [ now ] .relation = ( ani [ now ] .relation + ani [ now.parent ] .relation ) % 3;   //通过枚举儿子,父亲,及父亲的祖先节点的关系(这里的关系就是a == b, a 吃 b, b 吃 a)

2.在集合合并的时候,如下图,当前的两个节点是x, y,那么合并x,y所在集合,在普通并查集里面就是将y的祖先节点,也就是b,指向x的祖先节点,也就是a(a, b都由1的路径压缩得到),但是我们怎么确定a 和 b 的吃与被吃间的关系呢?

由已经得到答案的问题1可以知道,当知道儿子与父亲关系,父亲与父亲的祖先关系,可以推得儿子与父亲的祖先的关系。而在这里,我们已经知道b是y 路径压缩过的祖先,直接当成父亲看也可以,根据b 与 y之间的关系(假设b 吃 y),那么逆推下,即看成 y 是 b的儿子,则b 与 y的关系 x 就变成 x = ( 3 - x ) %  3,那么就如下图一样,我们可以先有b(儿子)->y(父亲)>x(爷爷)的关系将b连在x上,再由b > x -> a的关系确定b 与 a的关系。 以上,将y 的祖先 b 的父亲指向x 的祖先 a,并确定b 与 a的关系,就成功的合并x, y两个点所在的集合了

再谈想法正确性:假设x, y所属的不相关两个集合里面的关系都是自洽的,那么合并两个集合之后得到的新集合是否还是自洽的?

我们假设 x 集合里面有个点z,与x所在集合是满足关系的,而与y所属集合里面有些点矛盾,例如w,那么由z, w之前就应该合并为同一个集合,而z, w,分别从属两个不相关的集合,显然矛盾,所以假设不成立

最后如何计算假话的数量? 我们只能从已知条件推得当前说的话是否成立,当前条件就是询问的两个点从属同一个集合,不从属的话直接合并两个点就行了。对于两种关系d = 1, d = 2,通过枚举得到,如果是同一个物种,则有x1 = x2,否则就是x1 = x2 + 1

#include <map>#include <set>#include <queue>#include <stack>#include <vector>#include <cmath>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;#define lson l, mid, rt << 1#define rson mid + 1, r, rt << 1 | 1#define pi acos(-1.0)#define eps 1e-8#define asd puts("sdasdasdasdasd");typedef long long ll;const int inf = 0x3f3f3f3f;const int N = 50050;struct node{int fa, re, x;//re for relation, 0 for same, 1 for father eat son, 2 for son eat father}a[N];int n, d, k;void init(){for( int i = 1; i <= n; ++i ){a[i].x = i;a[i].fa = i;a[i].re = 0;}}int dfs( node &t ){if( t.x == t.fa )return t.x;int fa = t.fa;//printf("Animal %d s Parent is %d\n",t.x,t.fa); t.fa = dfs( a[fa] );t.re = ( t.re + a[fa].re ) % 3;return t.fa;}void Union( int x, int y, int xx, int yy, int d ){a[yy].fa = xx;a[yy].re = ( (3 - a[y].re) + (d - 1) + a[x].re ) % 3;}int main(){//while( ~scanf("%d%d", &n, &k) )scanf("%d%d", &n, &k);{init();int x, y, ans = 0;while( k-- ){scanf("%d%d%d", &d, &x, &y);if( x > n || y > n || ( d == 2 && x == y ) ){ans++;continue;}int xx = dfs( a[x] );int yy = dfs( a[y] );if( xx != yy )// not same set{Union( x, y, xx, yy, d );continue;}if( d == 1 ){if( a[x].re != a[y].re )ans++;}else{if( ( a[y].re + 3 - a[x].re ) % 3 != 1 )ans++;}}printf("%d\n", ans);}return 0;}


0 0
原创粉丝点击