家谱(并查集)

来源:互联网 发布:安徵农业网络书屋 编辑:程序博客网 时间:2024/05/03 09:11

Description
现代的人对于本家族血统越来越感兴趣, 现在给出充足的父子关系, 请你编写程序找到 某个人的最早的祖先。
Input
输入文件由多行组成, 首先是一系列有关父子关系的描述, 其中每一组父子关系由二行 组成,用#name 的形式描写一组父子关系中的父亲的名字,用+name 的形式描写一组父子关 系中的儿子的名字;接下来用?name 的形式表示要求该人的最早的祖先;最后用单独的一个 $表示文件结束。规定每个人的名字都有且只有 6 个字符,而且首字母大写,且没有任意两 个人的名字相同。最多可能有 1000 组父子关系,总人数最多可能达到 50000 人,家谱中的 记载不超过 30 代。
Output
按照输入文件的要求顺序,求出每一个要找祖先的人的祖先,格式:本人的名字+一个 空格+祖先的名字+回车。
Sample Input
#George
+Rodney
#Arthur
+Gareth
+Walter
#Gareth
+Edward
?Edward
?Walter
?Rodney
?Arthur
$
Sample Output
Edward Arthur
Walter Arthur
Rodney George
Arthur Arthur 


一看这题,只需求最早的祖先,那么我们可以把有父子关系的一对和并即可。如果A是B的祖先,P[x]为x的祖先,那么P[B]=A。
(自己好好想想吧,如果不会并查集的,在网上看看)。
我们可以把每个人编号。用HASH表快速找出一个人前面有没有出现过即可。
标程:
const
p1=150000;
var
h:array [0..p1] of string;{HASH表,记录名字}
h1:array [0..p1] of longint; {HASH表,记录编号}
f:array [1..60000] of string;  {每个编号对应的名字}
p:array [1..60000] of longint;  {并查集}
s:string;
ch:char;
x,y,k,i:longint;
function hash(s:string):longint;
var
 i:longint;
begin
hash:=0;
 for i:=1 to length(s) do
   hash:=hash*4+ord(s[i]); 
{哈希函数,这玩意贼神奇,一开始不乘4,超时!
乘个3,极限过!乘了4,直接15MS!!!!}
end;

function fd(s:string):longint;{查找}
var x:longint;
begin
 x:=hash(s);
 while h[x]<>'' do
  begin
   if h[x]=s then exit(h1[x]);{找到退出编号}
   inc(x);
   if x>p1 then x:=0;
  end;
 exit(0); {找不到退出0}
end;

procedure ins(s:string;a:longint);{插入}
 var i,x:longint;
begin
x:=hash(s);
while h[x]<>'' do
 begin
   inc(x);
   if x>p1 then x:=0;
 end;
h[x]:=s; h1[x]:=a; {找到空位x后记录数据}
end;

function find(x:longint):longint;{并查集+路径压缩}
var y,root,w:longint;
begin
y:=x;
  while p[y]>0 do
   y:=p[y];
  root:=y;
  y:=x;
  while p[y]>0 do
   begin
    w:=p[y];
    p[y]:=root;
    y:=w;
   end;
  find:=root;
end;

procedure union(x,y:longint);{合并,x是父亲,y是儿子}
var
 u,v:longint;
begin
u:=find(x);
v:=find(y);
p[v]:=u; {把儿子的祖先接到y上}
end;

begin
for i:=1 to p1 do 
begin h[i]:=''; h1[i]:=0; end;
repeat
 readln(s);
 if s='$' then break;
 ch:=s[1]; delete(s,1,1); {s删掉第一个字符是人名}
 x:=fd(s); {查找}
 if x=0 then {找不到给当前人编号,并插入HASH表}
  begin inc(k); f[k]:=s; x:=k; ins(s,k); end;
 if ch='#' then
   y:=x; {记录父亲的编号}
 if ch='+' then
   union(y,x); {合并父亲和儿子}
 if ch='?' then
  begin
   writeln(s,' ',f[find(x)]);{查找x的最早的祖先,输出其姓名}
  end;
until 1=2;
end.



1 0