AcWing 240 食物链

题目描述:

动物王国中有三类动物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和K句话,输出假话的总数。

输入格式

第一行是两个整数N和K,以一个空格分隔。

以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。

若D=1,则表示X和Y是同类。

若D=2,则表示X吃Y。

输出格式

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

数据范围

1≤N≤50000,
0≤K≤100000

输入样例:

100 7
1 101 1 
2 1 2
2 2 3 
2 3 3 
1 1 3 
2 3 1 
1 5 5

输出样例:

3

分析:

本题考察并查集扩展域,用于维护集合间传递性关系。为了维持不同节点间的关系,我们需要对并查集的边加上权重,通过边权的比较得出节点间的关系,或者说,通过节点到根节点之间的距离来确定关系。在分析本题前,先介绍下扩展域的查找操作。

我们知道,一般并查集的查找操作是:

int find(int x){
    if(x != fa[x])   fa[x] = find(fa[x]);
    return fa[x];
}

在查找根节点的同时将沿途遇见所有的节点的父节点都设为根节点,实现路径压缩。而带扩展域并查集查找根节点的操作是:

int find(int x){
    if(x != fa[x]){
        int t = find(fa[x]);
        d[x] += d[fa[x]];
        fa[x] = t;
    }   
    return fa[x];
}

一共做了三步操作,存下并查集的根节点,将节点x到父节点的权重加上其父节点到其父节点的权重,由于是递归实现的,例如根节点为1,1->2为1,2->3为2,3->4为3,开始执行的是d[3] = d[3] + d[2] = 3,之后d[4] = d[4] + d[3] = 6。这样一来,查找的同时就将沿途所有节点到根节点的距离都更新成功了,并且最后一步同样的将各个节点的父节点都直接指向根节点,再次实现了路径压缩。

对于本题,我们可以将这三类动物都维持到一棵树里,a吃b,b吃c,c吃a,是一个三角形,由于有三条边,我们可以通过改变各个节点到根节点的距离来实现关系的判断。比如d[a] = 0,d[b] =2,d[c] = 1,当(d[x]-d[y]) % 3=1,表示x被y吃,当x和y是同类时(d[x] - d[y])%3 = 0,如此便可维护处本题的传递性关系。

对于某句话x和y是同类,首先find下x,y的根节点u和v,当u=v时表示已经加入到一棵树里了,然后判断下(d[x]-d[y]) % 3是否为0,不为0说明不是同类,是假话。当u != v时说明二者还未构建关系,我们需要构建a和b的同类关系,首先加u集合加入到v集合,即fa[u] = v;然后我们知道d[a]是a到原根节点u的距离,d[b]是b到v的距离,我们需要想办法更新下u到v的距离来实现d[a] - d[b]是3的倍数。a到v的距离为d[a] + d[u],更新后ab的距离为(d[a] + d[u] - d[b]),要想其mod 3 = 0,只需要d[u] = d[b] - d[a]即可。也就是说,我们将u加入到v的时候,更新d[u] = d[b] - d[a]就间接的构建了a和b的同类关系。

对于某句话x吃y,同样先判断u是否等于v,如果相等说明加入到一棵树里了,再判断下(d[a] - d[b] - 1)%3是否为0,不为0就是假话。如果u != v.和上面一样,先将u加入到v集合里,要想(d[a] + d[u] - d[b] - 1) % 3 == 0(维持a吃b的关系),只需要更新d[u] = d[b] - d[a] + 1即可。

总的代码如下:

#include 
using namespace std;
const int maxn = 50005;
int fa[maxn],d[maxn],m,n;
int find(int x){
    if(x != fa[x]){
        int t = find(fa[x]);
        d[x] += d[fa[x]];
        fa[x] = t;
    }   
    return fa[x];
}
int main(){
    cin>>n>>m;
    for(int i = 1;i <= n;i++)   fa[i] = i;
    int op,a,b,res = 0;
    while(m--){
        cin>>op>>a>>b;
        if(a > n || b > n){
            res++;
            continue;
        } 
        int u = find(a),v = find(b);
        if(op == 1){
            if(u == v && (d[a] - d[b]) % 3)  res++;
            else if(u != v){
                fa[u] = v;
                d[u] = d[b] - d[a];
            }
        }  
        else{
            if(u == v && (d[a] - d[b] - 1) % 3) res++;
            else if(u != v){
                fa[u] = v;
                d[u] = d[b] - d[a] + 1;
            }
        }
    }
    cout<

 

你可能感兴趣的:(算法基础课,并查集,扩展域)