Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 39679 | Accepted: 12195 |
Description
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.
题目大意:一共有两种犯罪团伙,请你编程判断询问的两个犯罪嫌疑人是否在一个团伙,或者不知道。
两种操作,对于A X Y表示查询XY是否是一个团伙的。对于D X Y 表示X和Y不在一个团伙。
构建思路过程+详解:
对于给定操作D X Y 我们能够知道X Y不属于一个团伙,我们知道,普通的并查集是将一些个元素合并在一个集合里边,通常用来表示一个集合。但是现在给出的条件是不同的团伙,一时间显得有些蒙蔽。。。。不锅不要担心,我们线性考虑一下这个问题我们就会有思路的构建点:
假如我们有两个已知条件,1和2属于不同团伙,2和3属于不同同伙,显然,1和3是同伙并且和2不是同伙。这个时候我们其实还是可以用一个并查集将这已知的两条无向边都建入一个集和中,那么为了区分团伙,我们需要对其边进行赋值。12赋值为1,23赋值为1,假设我们在合并的时候选取1为根节点,那么13权值为2.不难发现,如果是一个团伙,其边权值为偶数,如果不是一个同伙,边权值为奇数。并且我们只要选取了每个节点作为参考点,对于其他子节点都计算从子节点到根节点的距离,那么就可以算出子节点和子节点之间的关系了。
那么我们如何处理合并操作呢?我们已知a,b权值为1,假设a到其根节点A距离为suma,b到其根节点B距离为sumb,我们能够简单的画出这样一个图:
显而易见,1+sumb==x+suma,这样我们就能知道x的值为1+sumb-suma。
那么求子节点到子节点的距离也很容易(当然是一定要在一个集合里边):suma-sumb即可。如果其值为偶数,那么说明他们是同伙,否则为非同伙。
注意点:当N==2的时候直接询问A 1 2的时候要输出different,因为题干中有说明每个团伙至少有一个人。
AC代码:
#include<stdio.h> #include<string.h> using namespace std; int sum[100050]; int f[100050]; int vis[100050]; int find(int x) { if(x!=f[x]) { int pre=f[x];//pre是x的一个父节点。 f[x]=find(f[x]);//递归找祖先。 sum[x]+=sum[pre];//路径压缩部分。 } return f[x]; } int main() { int t; scanf("%d",&t); while(t--) { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { f[i]=i; sum[i]=0; } while(m--) { char s[5]; scanf("%s",s); int x,y; scanf("%d%d",&x,&y); if(s[0]=='A') { int xx=find(x); int yy=find(y); if(xx==yy) { if((sum[y]-sum[x])%2==0) { printf("In the same gang.\n"); } else printf("In different gangs.\n"); } else { if(n==2) printf("In different gangs.\n"); else printf("Not sure yet.\n"); } } if(s[0]=='D') { int xx=find(x); int yy=find(y); if(xx!=yy) { sum[yy]=1-sum[y]+sum[x]; f[yy]=xx; } } } } }