题目:UVALive 6187
题型:数据结构
题意:
n个人,m个操作,操作方式有两种:
! a b w 表示b比a高w;
? a b 询问b比a高多少(可以是负数),如果无法得到答案,输出“UNKNOWN”。
分析:
由于n和m范围为10^6,所以建图之后每次更新之后求最短路是行不通的,所以为了压缩路径以提高查询效率,需要采用并查集。
我们设rank(x)为x节点到其根节点的差值,查找x的祖先的时候,每次递归都更新当前节点的rank值。
有点绕脑子的地方就是合并两个集合。对于更新操作! a b w,如果a和b不在同一个集合,需要将这两个集合合并。
设roota为a的祖先,rootb为b的祖先,那么有rank(rootb) = w + rank(a) - rank(b),这样两个集合的rank值就一致了。
代码:
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#define MAXN 1234567
using namespace std;
int n,m;
char ch[3];
int father[MAXN];//father[x]表示x的父节点
int rank[MAXN];//rank[x]表示x的秩
void Make_Set(int x){//初始化
father[x] = x;
rank[x] = 0;
}
int Find_Set(int x){
if(x != father[x]){
int tmp = Find_Set(father[x]);
rank[x] += rank[father[x]];
father[x] = tmp;
}
return father[x];
}
void Union(int a,int b,int w){
int roota = Find_Set(a);
int rootb = Find_Set(b);
if(roota == rootb) return;
else{
rank[rootb] = w + rank[a] - rank[b];
father[rootb] = father[roota];
}
}
int main(){
int a,b,w;
while(1){
scanf("%d%d",&n,&m);
if(n == 0 && m == 0) break;
for(int i=0;i<=n;i++){
Make_Set(i);
}
while(m--){
scanf("%s",ch);
if(ch[0] == '!'){
scanf("%d%d%d",&a,&b,&w);
Union(a,b,w);
}
else{
scanf("%d%d",&a,&b);
int aa = Find_Set(a);
int bb = Find_Set(b);
if(aa == bb){
printf("%d\n",rank[b] - rank[a]);
}
else{
puts("UNKNOWN");
}
}
}
}
return 0;
}