动物王国中有三类动物 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 句话有的是真
的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
• 当前的话与前面的某些真的话冲突,就是假话
• 当前的话中 X 或 Y 比 N 大,就是假话
• 当前的话表示 X 吃 X,就是假话
你的任务是根据给定的 N 和 K 句话,输出假话的总数。
输入输出格式
输入格式:
从 eat.in 中输入数据
第一行两个整数,N,K,表示有 N 个动物,K 句话。
第二行开始每行一句话(按照题目要求,见样例)
输出格式:
输出到 eat.out 中
一行,一个整数,表示假话的总数。
输入输出样例
输入样例#1: 复制
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
输出样例#1: 复制
3
说明
1 ≤ N ≤ 5 ∗ 10^4
1 ≤ K ≤ 10^5
解题思路:除了原来的f数组,我们新增一个d数组,用0 1 2 分别表示当前节点被父节点吃 跟父节点同类和吃父节点。
在路径压缩的时候就能发现子节点与父节点存在一个%3的关系。另外需要注意的就是节点合并的时候需要分情况讨论父节点的关系。
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define sca(x) scanf("%d",&x)
#define per(i,j,k) for(int i=j;i>=k;i--)
#define inf 0x3f3f3f3f
#define mod 1000000007
#define LL long long
#define N 50005
int f[N],r[N];
int F(int x)
{
if(f[x]==x) return x;
int fa=f[x];
f[x]=F(f[x]);
r[x]=(r[x]+r[fa])%3;
return f[x];
}
int U(int ff,int x,int y)
{
int f1=F(x);
int f2=F(y);
if(ff==1)
{
if(f1==f2)
{
if(r[x]!=r[y])return 0;
return 1;
}
else
{
f[f1]=f2;
if(r[x]==0)r[f1]=r[y];
else if(r[x]==1&&r[y]==1)r[f1]=0;
else if(r[x]==1&&r[y]==0)r[f1]=2;
else if(r[x]==1&&r[y]==2)r[f1]=1;
else if(r[x]==2&&r[y]==0)r[f1]=1;
else if(r[x]==2&&r[y]==1)r[f1]=2;
else if(r[x]==2&&r[y]==2)r[f1]=0;
return 1;
}
}
if(ff==2)
{
if(f1==f2)
{
if(r[x]==0&&r[y]!=1)return 0;
if(r[x]==1&&r[y]!=2)return 0;
if(r[x]==2&&r[y]!=0)return 0;
else f[f1]=f2;
return 1;
}
else
{
f[f1]=f2;
if(r[x]==0&&r[y]==0)r[f1]=2;
else if(r[x]==0&&r[y]==1)r[f1]=0;
else if(r[x]==0&&r[y]==2)r[f1]=1;
else if(r[x]==1&&r[y]==0)r[f1]=1;
else if(r[x]==1&&r[y]==1)r[f1]=2;
else if(r[x]==1&&r[y]==2)r[f1]=0;
else if(r[x]==2&&r[y]==0)r[f1]=0;
else if(r[x]==2&&r[y]==1)r[f1]=1;
else if(r[x]==2&&r[y]==2)r[f1]=2;
}
return 1;
}
return 1;
}
int main()
{
int n,k;
scanf("%d%d",&n,&k);
int ff,x,y;
int sum=0;
for(int i=1;i<=n;i++)f[i]=i,r[i]=0;
while(k--)
{
scanf("%d%d%d",&ff,&x,&y);
if(x>n||y>n)
{
sum++;
continue;
}
if(ff==2&&x==y){
sum++;
continue;
}
if (!U(ff,x,y))
{
sum++;
}
}
cout<<sum<<endl;
}
上边的知识为了理解一般并查集中间的过程,代码比较长且需要重复的分情况讨论。
后来学习了一个简单的方法就是开三倍的数组空间维护的并查集 一倍存自己 2被存自己的吃的对象 3被存天敌;
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define sca(x) scanf("%d",&x)
#define per(i,j,k) for(int i=j;i>=k;i--)
#define inf 0x3f3f3f3f
#define mod 1000000007
#define LL long long
#define N 50005
int f[N*3];
int F(int x)
{
return f[x]==x?x:f[x]=F(f[x]);
}
int main()
{
int n,k;
sca(n),sca(k);
rep(i,1,3*n)f[i]=i;
int op,u,v,ans=0;
rep(i,1,k)
{
scanf("%d%d%d",&op,&u,&v);
if(u>n||v>n)
{
ans++;
continue;
}
if(op==2&&u==v)
{
ans++;
continue;
}
if(op==1)
{
if(F(u)==F(v+n)||F(v)==F(u+n))ans++;//如果u吃v或者v吃u
else
{
rep(j,0,2)f[F(u+n*j)]=F(v+n*j);//合并
}
}
if(op==2)
{
if(F(u)==F(v)||F(v)==F(u+n))ans++;//如果u与v同类或者v吃u
else
{
f[F(u)]=F(v+n);
f[F(u+n)]=F(v+2*n);
f[F(u+2*n)]=F(v);
}
}
}
cout<<ans<<endl;
}
基于第一种做法,有一个利用向量思想快速确定两个 父亲节点的关系的方法。
例如:
如图利用向量的思想我们知道x与f2的关系为(op+d[y])%3=d[x]+d[f1]
所以d[f1]=(3-d[x]+d[y]+(x与y的关系))%3;
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define sca(x) scanf("%d",&x)
#define per(i,j,k) for(int i=j;i>=k;i--)
#define inf 0x3f3f3f3f
#define mod 1000000007
#define LL long long
#define N 50005
int f[N];
int d[N];
int F(int x)
{
if(f[x]==x)return x;
int fa=f[x];
f[x]=F(f[x]);
d[x]=(d[x]+d[fa])%3;
return f[x];
}
int U(int op,int x,int y)
{
int f1=F(x);
int f2=F(y);
if(op==1)
{
if(f1==f2&&d[x]!=d[y])return 1;
else
{
f[f1]=f2;
d[f1]=(3-d[x]+d[y])%3;
return 0;
}
}
if(op==2)
{
if(f1==f2)
{
if(d[x]!=(d[y]+1)%3)return 1;
else return 0;
}
else
{
f[f1]=f2;
d[f1]=(3-d[x]+d[y]+1)%3;
return 0;
}
}
return 0;
}
int main()
{
int n,k;
sca(n),sca(k);
rep(i,1,n)f[i]=i,d[i]=0;
int op,u,v,ans=0;
rep(i,1,k)
{
scanf("%d%d%d",&op,&u,&v);
if(u>n||v>n)
{
ans++;
continue;
}
if(op==2&&u==v)
{
ans++;
continue;
}
ans+=U(op,u,v);
}
cout<<ans<<endl;
}