题:
动物王国中有三类动物 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 ),输出假话的总数。
输入:
输入包括多组数据,
每组数据第一行是两个整数 N 和 K ,以一个空格分隔。
以下 K 行每行是三个正整数 D , X , Y ,两数之间用一个空格隔开,其中 D 表示说法的种类。
若 D=1 ,则表示 X 和 Y 是同类。
若 D=2 ,则表示 X 吃 Y 。
输入以EOF结尾
输出:对于每组数据,输出一行包含一个整数,表示假话的数目。
解法:
并查集,我是一边写代码一边写这篇博客的
并查集的实现
parent[i]表示i的父节点的编号
relation[i]表示i与父节点的关系
0 表示该节点和父节点是同类
-1 表示该节点吃父节点
1 表示该节点被父节点吃
以上三个值是我自己规定的
初始化为
for(int i=1;i<=n;i++)
{
parent[i]=i;//初始时父亲就是自己
relation[i]=0;//与自己的关系是同类
}
对于sample
数据做分析
110 7 1 101 1 2 1 2 2 2 3 2 3 3 1 1 3 2 3 1 1 5 5初始时,
编号 父亲 与父亲的关系
1 1 0
2 2 0
3 3 0
5 5 0
101 101 0
输入第一句话
1 101 1
说 101 和 1是同类
找到101和1的根节点就是101和1
他俩的关系是同类,于是在并查集中的结构为
其中的0表示101和1是同类
第二句话
2 1 2
表明1吃2
找到1和2的根节点分别为1和2
在并查集中的结构为
第三句话:
2 2 3
表明2吃3
而2和3的根节点分别为1和3
由于2到根节点的路径的权值为1,为正奇数,说明2被根节点吃,而2又吃3,故3吃1
那么1和3的关系为1被3吃,画图为
对此图,有
101到根节点的权为1 101被根节点吃 等效为1
2到根节点的权为2 2吃根节点 等效为-1
1到根节点的权为1 1被根节点吃 等效为0
对此进行分析
设节点i到其根节点的权为m
m=0*k1+1*k2+(-1)*k3
很明显在路径中间的0并不影响其与根节点的关系
由上面样例可以知道两个1等效为一个-1,同样两个-1也等效为一个1(这点随便画个图就知道)
三个1或-1就可以等效为0了(自己画图验证)
1和-1又可以等效为0(自己画图验证)
所以m%3完全可以代表该节点与根节点的关系,m%3=0等效为0,m%3=2等效为-1,m%3=1等效为1
在并查集的合并操作中,我们要合并a和b,假设ra和rb为a和b的根节点
ma与mb为带权路径值,代表着a和b分别与根节点的关系
对所有情况作分析
a与b的关系 a与ra的关系 b与rb的关系 ra与rb的关系
a吃b -1 1(a被ra吃) 1(b被rb吃) ra吃rb -1
a吃b -1 -1(a吃ra) -1(b吃rb ) ra吃rb -1
a吃b -1 0 0 ra吃rb -1
a吃b -1 1 -1 ra与rb同类 0
a吃b -1 -1 0 ra与rb同类 0
a吃b -1 0 1 ra与rb是同类 0
a吃b -1 -1 1 rb吃ra 1
a吃b -1 1 0 rb吃ra 1
a吃b -1 0 -1 想通了
以上的9个分析真是麻烦,作一次推理
a与b的关系为mab
a与rb的关系为mab+mb
a与ra的关系为ma
那么ra与rb的关系必为mab+mb-ma,用以上九个例子验证通过
接下来我去码代码了,AC后贴代码
AC了,贴代码
在BIT要AC,你要写while(scanf("%d %d",&n,&k))
在POJ要AC,你要写scanf("%d %d",&n,&k);
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int parent[50010];
int relation[50010];
int counter;
struct T
{
int num;//根节点的编号
int quan;//到根节点的权
};
T root(int i)
{
T ans;
if(parent[i]==i)
{
ans.num=i;
ans.quan=0;
return ans;
}
T tt=root(parent[i]);
parent[i]=tt.num;//路径压缩
relation[i]+=tt.quan;
relation[i]%=3;
while(relation[i]<0)
{
relation[i]+=3;
}
if(relation[i]==2)
{
relation[i]=-1;
}
ans.num=parent[i];
ans.quan=relation[i];
return ans;
}
void Merge(int mab,int a,int b)
{
T tempa=root(a);
T tempb=root(b);
int ra=tempa.num;
int rb=tempb.num;
int ma=tempa.quan;
int mb=tempb.quan;
parent[ra]=rb;
int tt=mab+mb-ma;
tt%=3;
while(tt<0)
{
tt+=3;
}
if(tt==2)
{
tt=-1;
}
if(ra==rb&&tt!=0)//冲突了
{
counter++;
return;
}
if(ra!=rb)
{
relation[ra]=tt;
}
}
int main()
{
int n,k;
while(scanf("%d %d",&n,&k))
{
for(int i=1;i<=n;i++)
{
parent[i]=i;
relation[i]=0;
}
counter=0;//假话的数目
while(k--)
{
int D,X,Y;
scanf("%d %d %d",&D,&X,&Y);
if(X>n||Y>n)//满足第二条规则,假话
{
counter++;
continue;
}
if(D==2&&X==Y)//满足第三条规则,假话
{
counter++;
continue;
}
if(D==1&&X==Y)
{
continue;
}
if(D==1)
{
D=0;//X与Y是同类
}
if(D==2)
{
D=-1;//X吃Y
}
Merge(D,X,Y);
}
printf("%d\n",counter);
}
return 0;
}