题目描述:
动物王国中有三类动物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<