POJ1182食物链之并查集解法

来源:互联网 发布:手机剪裁照片的软件 编辑:程序博客网 时间:2024/06/06 02:10

食物链
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 74500 Accepted: 22124
Description

动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是”1 X Y”,表示X和Y是同类。
第二种说法是”2 X Y”,表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。
Input

第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
Output

只有一个整数,表示假话的数目。
Sample Input

100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
Sample Output

3
Source

分析:题意就是每次输入一组关系,然后判断这个关系的真假,最后输出假关系的数目.我们可以用数组范围表示,X[2]表示2属于A,X[2+N]表示2属于B,X[2+2N]表示2属于C,3用Y的表示同理.
并查集中的每个组中的元素都有一种关系,本题中这种关系就是:情况的同时发生,如X[2]和Y[3+N]在一个组里,表示2为A的时候3一定为B,这样就表示清楚了2和3的捕食关系.
由于我们不知道2属于哪一类,因此操作的时候3中情况都要考虑到

首先看一下并查集的基本操作int par[150001];int ran[150001];//初始化n个元素void init(int l){    for (int i = 1; i <= l; i += 1){        par[i] = i;        ran[i] = 0;    }}//判断元素的根int root(int x){    //递归搜索,长处:结构清晰,可记忆化搜索    if (par[x] == x){        return x;    }else{        //记忆化搜索,回溯过程中所有节点的父节点直接指向根节点        return par[x] = root(par[x]);    }    //非递归搜索,长处:效率高,存储空间小    //while (x != par[x]) x = par[x];    //return x;}//判断两个元素是否同组bool same(int x,int y){    return root(x) == root(y);}//合并两个组void unite(int x,int y){    x = root(x);    y = root(y);    if (x == y) return ;    if (ran[x] < ran[y]){        par[x] = y;    }else{        par[y] = x;        if (ran[x] == ran[y]) ran[x]++;    }}

h下面是代码:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;int N,K;int D[150001],X[150001],Y[150001];int par[150001];int ran[150001];void init(int l){    for (int i = 1; i <= l; i += 1){        par[i] = i;        ran[i] = 0;    }}int root(int x){    if (par[x] == x){        return x;    }else{        return par[x] = root(par[x]);    }}bool same(int x,int y){    return root(x) == root(y);}void unite(int x,int y){    x = root(x);    y = root(y);    if (x == y) return ;    if (ran[x] < ran[y]){        par[x] = y;    }else{        par[y] = x;        if (ran[x] == ran[y]) ran[x]++;    }}void solve(){    int ans = 0;    init(3*N);    for (int j = 0; j < K; j += 1){        int d = D[j];        int x = X[j];        int y = Y[j];        if (x < 0 || y < 0 || x > N || y > N){            ans++;        }        else if (d == 1){            if (same(x,y+N) || same(x,y+2*N)){                ans++;            }else{                unite(x,y);                unite(x+N,y+N);                unite(x+2*N,y+2*N);            }        }        else{            if (same(x,y) || same(x,y+2*N)){                ans++;            }else{                unite(x,y+N);                unite(x+N,y+2*N);                unite(x+2*N,y);            }        }    }    printf("%d\n",ans);}int main(){    scanf("%d%d",&N,&K);    for (int i = 0; i < K; i += 1)        scanf("%d%d%d",&D[i],&X[i],&Y[i]);    solve();    return 0;}
原创粉丝点击