Find them, Catch them
Time Limit: 1000MS Memory Limit: 10000KB 64bit IO Format: %I64d & %I64u
Submit
Status
Description
The police office in Tadu City decides to say ends to the chaos, as launch actions to root up the TWO gangs in the city, Gang Dragon and Gang Snake. However, the police first needs to identify which gang a criminal belongs to. The present question is, given two criminals; do they belong to a same clan? You must give your judgment based on incomplete information. (Since the gangsters are always acting secretly.)
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:
D [a] [b]
where [a] and [b] are the numbers of two criminals, and they belong to different gangs.
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
The first line of the input contains a single integer T (1 <= T <= 20), the number of test cases. Then T cases follow. Each test case begins with a line with two integers N and M, followed by M lines each containing one message as described above.
Output
For each message “A [a] [b]” in each case, your program should give the judgment based on the information got before. The answers might be one of “In the same gang.”, “In different gangs.” and “Not sure yet.”
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.
题意:输入n和m,代表有n个人和m个询问,n个人分别属于两个帮派,注意是只有两个帮派,接下来输入m行指令:
A a b第一个字母为A代表询问指令,询问输出a与b是否在同一个帮派。D a b代表告诉你a和b是不同帮派的成员。
哇这个题 ,第一次遇到类似的题目是poj的另一道题a bug’s life,当时我是用二分图水过,看到别人说用并查集时我一脸懵逼 然而这道题我却突然想用并查集写,然鹅没写出来…于是乎看题解…
其实是这样的,我一开始想用并查集维护两个种类,但是仔细想了想这样不行,因为加入告诉你A和B,C和D,E和F都不是同一帮派,那么仅维护两个种类是不行的。所以只能还是用边表示一对成员的关系,只不过相邻的边代表不同帮派的成员。比如输入D 1 2,还是令1指向2,再输入D 3 4,令3指向4,这样建好图之后,相邻的结点属于不同成员,但是隔一个结点的又属于相同成员。为了直观地表示各个成员于是就可以用二分图染色,相邻的成员颜色不同。但是这题的数据量,用二分图怕是会超时,只能用并查集。并查集还是很高效的呢。
如果用二分图,需要从1点遍历到2点,记录下中间的结点个数然后判断奇偶,才能计算出这两点之间的关系。为了高效,我们可以直接利用一个数组来记录某点与根结点的关系,即某点与根结点是否输入一个帮派。于是乎,并查集我开了两个数组,root来记录根结点是哪个,same来记录与根结点的关系。0代表相同,1代表不同。
路径压缩。在路径压缩的时候,边的奇偶数就被破坏了,因此需要维护same数组。那么如何判断same数组的值呢?假设某点x就是根结点,那么不谈了,same[x]=0。如果不是根结点,我们只能通过x的父节点root[x]与根结点的关系来确定x结点与根结点的关系。那么就有:
same[x] same[root[x]] 新的same[x]
0 0 0 x与其父节点同帮,x的父节点与x根结点同帮,则x与其根结点同帮。
1 0 1 x与其父节点不同帮,x的父节点与x根结点同帮,则x与其根结点不同帮。
0 1 1 x与其父节点同帮,x的父节点与x根结点不同帮,则x与其根结点不同帮。
1 1 0 x与其父节点不同帮,x的父节点与x根结点不同帮,则x与其根结点同帮。
看起来很像异或啊。不过有一点需要注意,我们在判断的时候需要x的父节点与根结点的关系,因此我们需要在判断之前就把x父节点的find操作做了。find函数如下:
int find(int x)
{
if(root[x]==-1) return x;
else
{
int temp=root[x];
root[x]=find(root[x]);
same[x]=same[x]^same[temp];
return root[x];
}
}
再说合并操作。给定一个指令D a b,需要首先找到根结点fa,fb,并且a,b和根结点的关系same[a]和same[b]都更新好了。对于root,很简单,令root[fa]=fb即可,那么same[fa]呢?如果判断fa和fb的关系嘞,由于一直a和b不同帮,我们还是穷举看下:
same[a] same[b] same[fa]
0 0 1 a与fa同帮,b与fb同帮,因此fa与fb不同帮
0 1 0 a与fa同帮,b与fb不同帮,因此fa与fb同帮
1 0 1 a与fa不同帮,b与fb同帮,因此fa与fb不同帮
1 1 0 a与fa不同帮,b与fb不同帮,因此fa与fb同帮
这就是异或非啊。因此代码就很简单了:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define inf 99999999
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
int t,n,m,root[100010],same[100100];
int find(int x)
{
if(root[x]==-1) return x;
else
{
int temp=root[x];
root[x]=find(root[x]);
same[x]=same[x]^same[temp];
return root[x];
}
}
int main()
{
cin>>t;
while(t--)
{
scanf("%d%d",&n,&m);
char c;
int a,b;
memset(root,-1,sizeof(root));
memset(same,0,sizeof(same));
for(int i=1;i<=m;i++)
{
scanf("%s%d%d",&c,&a,&b);
int fa=find(a),fb=find(b);
if(c=='D')
{
if(fa!=fb)
{
root[fa]=fb;
same[fa]=~(same[a]^same[b]);
}
}
if(c=='A')
{
if(fa!=fb)cout<<"Not sure yet."<<endl;
else if(same[a]==same[b])cout<<"In the same gang."<<endl;
else if(same[a]!=same[b])cout<<"In different gangs."<<endl;
}
}
}
}