POJ 1182 食物链(带权并查集)

来源:互联网 发布:linux history 清除 编辑:程序博客网 时间:2024/05/16 16:57

原来不理解这题怎么做,今天看了一下题解终于理解了。
本文转载自:POJ 1182:食物链[详细!]

题意:

A,B,C三种动物,A吃B, B吃C,C吃A。有n个动物,他们编号为1~n。
输入:第一行n,k,分别表示动物个数,给出k句话(有真有假)。接下来n行每行一句话,每句的格式为三个整数:d,x,y。x,y为动物编号,d为1时表示x,y是同类,d为2时表示x吃y。
说明:假话有三种: 1) 当前的话与前面的某些真的话冲突,就是假话; 2) 当前的话中X或Y比N大,就是假话; 3) 当前的话表示X吃X,就是假话。
输出:假话的个数。

解析:

一、relation的确定

对每一个元素,把它对父节点的关系用数组r[i]表示,即relation,作为权值。
由于数字d指定了后面给出的两个动物的关系:
1) d=1时,d1=0,x,y是同类;
2) d=2时,d1=1,x吃y
这个权值不是随便定的噢,且看下面推导~!
因此我们设r[i]包含这三种值,表示动物之间的三种关系:
1) r[i]=0时,ip[i]是同类;
2) r[i]=1时,ip[i]吃;
3) r[i]=2时,ip[i]
注: p[i]为i的父节点。

二、路径压缩 find_set(x)的节点算法

我们知道,并查集的集合代表元是一个元素,这里规定集合出现的第一个元素是代表元。
为了使查找效率提高,我们需要使树尽可能低,并查集的路径压缩让它只有一层。
那么怎么把将不是直接联系的两个节点成为父子关系?怎样表明他们之间的关系呢?
这是我们接下来要推导的东西

根据儿子对父亲的关系r,和父亲对爷爷的关系r推出儿子对爷爷的关系r

           i      j          k     爷爷  父亲   儿子      儿子与爷爷            0      0       0=(i + j)%3             0      1       1=(i + j)%3             0      2       2=(i + j)%3           1      0       1=(i + j)%3            1      1       2=(i + j)%3            1      2       0=(i + j)%3            2      0       2=(i + j)%3            2      1       0=(i + j)%3            2      2       1=(i + j)%3

观察即可知道,儿子对爷爷的关系是k=(i+j)%3。
为啥要这样做?
可以画个图理解下。如:X->Y,Y->Z,要把X的父节点设为Z就需要上述步骤。
需要注意的是,每次更新节点的r[i]时应该先找到父亲节点再更新,否则会因为给出的话是假话而发生错误。

三、集合间关系的确定

初始时候每个元素都是自洽的,那么有几个集合的时候,怎么确定集合(代表节点)之间的关系?
因为只有确定两个集合(的代表节点)之间的关系才能把两个集合合并最后得到一个并查集。

先给出公式:r[ry]=(3r[y]+(d1)+r[x])

我们分3个小部分来推导:
(例如,由Y>X>P,Y>Q,得到Q>P。我们需要先得到Q>X,再得到Q>P)画图更易理解。

(1) 由子对父的关系得到父对子的关系
子对父
0(父子同类) (30)%3=0
1(父吃子) (31)%3=2 //父吃子
2(子吃父) (32)%3=1 //子吃父,一样的哦亲
r[]=(r[]+r[])

(2) Q>X,把Y接到X上,使Q成为Y的儿子,进而得到Q对X的relation权值:
r[QX]=(r[YX]+r[QY])%3
r[YX]=d1
r[QY]=3r[Y]
r[QX]=((d1)+(3r[y]))%3

(3) Q>P,最后由Q>X>P得到Q>P(得到Q对P的 relation 权值:
r[QX]=(r[YX]+r[QY])
r[QP]=(r[YX]+r[QY]+r[XP])
r[QP]=((d1)+(3r[y])+r[x])

注意:

题目只有一组样例,如果按照多组来输入会WA。

my code

#include <cstdio>#include <algorithm>using namespace std;const int N = (int)5e4 + 10;int pa[N], r[N];int ans;int n, K;void init() {    for(int i = 0; i <= n; i++) {        pa[i] = i;        r[i] = 0;    }}int find(int u) {    if(u == pa[u]) return u;    int pre = pa[u];    pa[u] = find(pre);    r[u] = (r[u] + r[pre]) % 3;    return pa[u];}void Union(int x, int y, int d) {    int root1 = find(x);    int root2 = find(y);    if(root1 != root2) {        pa[root2] = root1;        r[root2] = (3 + (d - 1) + r[x] - r[y]) % 3;    }else {        if(d == 1 && r[x] != r[y])            ans++;        else if(d == 2 && (3 - r[x] + r[y]) % 3 != 1)            ans++;    }}int main() {    int x, y, d;    scanf("%d%d", &n, &K);    init();    ans = 0;    while(K--) {        scanf("%d%d%d", &d, &x, &y);        if(x > n || y > n) {            ans++;        }else if(d == 2 && x == y) {            ans++;        }else {            Union(x, y, d);        }    }    printf("%d\n", ans);    return 0;}
0 0
原创粉丝点击