POJ 1703 Find them, Catch them(路径压缩并查集)
http://poj.org/problem?id=1703
题意:
一个城市由N个坏人,他们分别属于两个帮派.且这两个帮派里面最少都有1个人.现在给出如下两种语句:
D a b 表示a和b肯定不在一个帮派
A a b 你需要回答a和b的关系.
输入:首先是一个T (1 <= T <= 20),表示实例个数.每个实例第一行为N (N <= 10^5)和M (M <= 10^5),接下来是M条D和A语句.
输出:回答每条A语句。
分析:
带路径压缩的并查集:
a与其父亲同帮派:r[a]=0
a与其父亲不同帮派:r[a]=1
在得到D x y 语句后 且 x和y所属的连通分量不同时 合并他们所属的树根时,要小心验证.
本题新代码依然是利用食物链那题的关系传递函数来做的而不是推到公式做的。
注意:题目说每个帮派最少有1个人,那么会不会出现说N个人,输入的关系前N-1个人都在同一个帮派,但是最后一个人没有出现过,然后问你最后一个人和前面某个人的关系?当N==2时可能出现,N>=3时不可能出现上述情况,因为题中给的只有D关系,必然一有关系就是两个人各在不同的帮派了。
AC代码(新):375ms
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=100000+5;
int fa[maxn];
int r[maxn]; //关系
int rev(int v)
{
return v;
}
int rela_2(int v1,int v2)
{
if(v1==0) return v2;
if(v2==0) return v1;
if(v1==1 && v2==1) return 0;
}
int rela_3(int v1,int v2,int v3)
{
return rela_2(rela_2(v1,v2),v3);
}
int findset(int x)
{
if(fa[x]==-1) return x;
int root=findset(fa[x]);
r[x] = rela_2(r[x] , r[fa[x]]);
return fa[x]=root;
}
int bind(int u,int v,int relation)
{
int fu=findset(u);
int fv=findset(v);
if(fu!=fv)
{
r[fu]= rela_3(rev(r[u]),relation,r[v]);
fa[fu]=fv;
return 1;
}
return 0;
}
int get_rela(int u,int v)//得到u与v的关系
{
int fu=findset(u);
int fv=findset(v);
if(fu!=fv) return -1;
return rela_2(r[u],rev(r[v]));
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,k;
scanf("%d%d",&n,&k);
memset(fa,-1,sizeof(fa));
memset(r,0,sizeof(r));
while(k--)
{
char str[10];
int u,v;
scanf("%s%d%d",str,&u,&v);
if(str[0]=='A')
{
int relation=get_rela(u,v);
if(relation==-1) printf("Not sure yet.\n");
else if(relation==0) printf("In the same gang.\n");
else if(relation==1) printf("In different gangs.\n");
}
else if(str[0]=='D')
{
bind(u,v,1);
}
}
}
return 0;
}
AC代码:360ms
<span style="font-size:18px;">#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
using namespace std;
const int MAXN = 100010;
int pa[MAXN];
int v[MAXN];
int findset(int x)
{
if(pa[x]==-1)return x;
int temp=findset(pa[x]);
v[x] = (v[x]+v[pa[x]])%2;
return pa[x] = temp;
}
void bind(int i,int j)
{
int fa= findset(i);
int fb=findset(j);
if(fa!=fb)//切记 这里一定要判断fa和fb是不是同一个连通分量,如果不判断将出现严重错误
{
pa[fb]=fa;
v[fb]=(v[i]-v[j]+3)%2;//测试得到
}
}
int main()
{
int T;
int n,m;
char str[10];
int a,b;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
memset(pa,-1,sizeof(pa));
memset(v,0,sizeof(v));
while(m--)
{
scanf("%s%d%d",str,&a,&b);
if(str[0]=='D')
{
bind(a,b);
}
else if(str[0]=='A')
{
if(a==b)//A a a
{
printf("In the same gang.\n");
continue;
}
if(findset(a)==findset(b))//同一颗树
{
if(v[a]==v[b])
printf("In the same gang.\n");
else if(v[a]!=v[b])
printf("In different gangs.\n");
}
else
{
if(n==2 && findset(a)!=findset(b))
printf("In different gangs.\n");
else
printf("Not sure yet.\n");
}
}
}
}
return 0;
}
</span>