Description
Assume N (N <= 10^5) criminals are currently in Tadu City, numbered from 1 to N. And of course, at least one of them belongs to Gang Dragon, and the same for Gang Snake. You will be given M (M <= 10^5) messages in sequence, which are in the following two kinds:
1. D [a] [b]
where [a] and [b] are the numbers of two criminals, and they belong to different gangs.
2. A [a] [b]
where [a] and [b] are the numbers of two criminals. This requires you to decide whether a and b belong to a same gang.
Input
Output
Sample Input
1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4
Sample Output
Not sure yet.
In different gangs.
In the same gang.
花了一些力气,终于把这个经典的题弄懂了,感谢TR学长的解答。
在这之前先拿一道经典的题抛砖引玉:
有N个人,M个关系,每个关系由a, b, c三个量描述,表示a与b的关系是c。当c为0时表示a,b是朋友,当c为1时表示a, b是敌人。已知关系具有传递性,也就是说a, b是朋友以及a, d是朋友的时候b, d是朋友。a, b是敌人以及a, d是朋友的时候b, d是敌人。问题给的M条关系是否成立,若成立则输出"YES",否则输出"NO",并输出最早在给出第几条关系的时候可以确定关系不成立。
(N, M <= 10^5, a, b <= N, a != b, c = 0, 1)
大概就是说:如果让并查集维护朋友关系是很简单的,a,b是朋友,b,c是朋友,用并查集的unit操作+find询问,很容易知道a,c是朋友,但是我该怎么维护敌人关系。我们考虑引入辅助元素,例如i是它本尊,i+N是它的宿敌。当然这个宿敌是不存在的,是我们虚拟出来的。
a,b是敌人的话,那么unit(a,b+N),unit(b,a+N),为什么?如果a和b的宿敌是朋友,那么a和b就是敌人了。记住,并查集永远只维护朋友关系。
a,b是朋友的话:同理,我就unit(a,b),unit(a+N,b+N)。
怎样判断信息是错误的?如果a和a的宿敌当朋友了,换句话说,就是与前面的信息矛盾了,就说明信息是错的。
这题自己的code:
#include<cstdio>
const int MAXN=1e5+5;
int par[2*MAXN];
int find(int x){
return par[x]==x?par[x]:find(par[x]);
}
void unit(int x,int y){
x=find(x);y=find(y);
if(x!=y)par[x]=y;
}
int main(){
int N,M;scanf("%d%d",&N,&M);
for(int i=0;i<2*N;++i)
par[i]=i;
int i;
for(i=0;i<M;++i){
int a,b,c;scanf("%d%d%d",&a,&b,&c);
if(c==0){
if(find(a)==find(b+N)||(find(a+N)==find(b)))break;
unit(a,b);
unit(a+N,b+N);
}
if(c==1){
if(find(a)==find(b)||find(a+N)==find(b+N))break;
unit(a,b+N);
unit(a+N,b);
}
}
printf("%d\n",i+1);
}
好了,如果上面的理解清楚了,那么这个题就不难了。
code:
#include<cstdio>
const int MAXN=1e5+5;
int par[MAXN*2];
int find(int x){
return par[x]==x?par[x]:par[x]=find(par[x]);
}
void unite(int x,int y){
x=find(x);y=find(y);
if(x!=y)par[x]=y;
}
int main(void){
int T;scanf("%d",&T);
while(T--){
int N,M;scanf("%d%d",&N,&M);getchar();
for(int i=0;i<2*N;++i)
par[i]=i;
for(int i=0;i<M;++i){
char ch;int a,b;
scanf("%c%d%d",&ch,&a,&b);getchar();
if(ch=='A'){
if(find(a)==find(b))printf("In the same gang.\n");
else if(find(a)==find(N+b))printf("In different gangs.\n");
else printf("Not sure yet.\n");
}else{
unite(a,b+N);
unite(a+N,b);
}
}
}
}