题:
动物王国中有三类动物 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; }