哈哈哈 第一天脑子疼没看懂,第二天一看,也就那么回事嘛。
动物王国中有三类动物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
虽然是三个状态,但其实也是两个状态,同类和X吃Y,三个状态指的是并查集里面结点之间的关系,有三个,而在整个问题里面只有同类和吃的关系。
如果判断一个人说的话真还是假,当然是之前有做过手脚的那些动物才能判断啦,所以把以前遇到动过手脚的放在同一个并查集里面。看了很多题解,有很详细也有很短的。忘了在哪个博客看到一句很对的话,带权并查集和一般并查集不同的地方在于一般并查集只是简单的合并,而带权并查集就是用值来代表关系,很明显食物链是一个环,里面有三个关系,A吃B,A被B吃,A和B是同类,(看第一句)不要用字面理解,脑子里面脑补一下一个三角图,就是孙子到底是吃爷爷还是被爷爷吃,还是和爷爷是同类啊(这个关系可以能孙子与爸爸的关系和爸爸与爷爷的关系推粗来,因为是在一个并查集里面,只有一个根,所以同一个根上每三个都是一个关系,而这种各个分开却又关联的并查集来表示,就是带权并查集,关键是路径的压缩。由于求余定理表明(a+b)%3和(a%3+b%3)%3是一致的,所以在路径压缩里面不用存唯一值,每次求余就行。。。。。如果不取余难道不行吗 只有最后取余难道不行吗?。答案是不行,刚wa两次,为什么会这样呢。。我只能勉强解释为,ran[i]他么真的有可能是负值,想想也对 如果有一个区间比较大,但是要往小的并,更新这个旧区间根结点的ran的时候要减去旧区间根节点的权值,一下子就变负的了(这个ran的更新最好是画一条数组,把根往右画,就能理解了,和昨天做的上一条并查集的题很像)) 贴代码。那么他们的关系就是一根树里面的结点到其中一个祖辈结点的距离%3,然后和d-1比较就行。
#include <string>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
typedef long long LL;
using namespace std;
const int Max=50010;
int far[Max],ran[Max];
int n;
int ans;
void init()
{
for(int i=0;i<=n;i++)
{
far[i]=i;
ran[i]=0;
}
}
int find(int v)
{
if(far[v]!=v)
{
int t=far[v];
far[v]=find(far[v]);
ran[v]=(ran[v]+ran[t])%3;
return far[v];
}
return v;
}
void union1(int d,int x,int y)
{
int t1=find(x);
int t2=find(y);
if(t1!=t2)
{
far[t1]=t2;
ran[t1]=(-ran[x]+d-1+ran[y]+3)%3; // 数轴 x t1 y t2 x到t1 为 ran[x]
} // y 到t2为 ran[y] d-1为x 到y 的距离
// ran永远表示的是该点到根节点的距离。
else
{
if((ran[x]-ran[y]+3)%3!=d-1)
ans++;
}
}
int main()
{
int m;
scanf("%d%d",&n,&m);
ans=0;
init();
int d,x,y;
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&d,&x,&y);
if(x>n||y>n)
ans++;
else
{
union1(d,x,y);
}
}
printf("%d\n", ans);
}