【NOIP2013模拟】Freda的传呼机
Time Limits: 100 ms Memory Limits: 131072 KB
Description
为了 随时 与 rainbow快速交流, Freda制造了 两部传呼机 。Freda和 rainbow所在的地方有N座房屋、M条双向 光缆 。每条光缆连接两座房屋, 传呼机发出的信号只能沿着光缆传递,并且 传呼机的信号 从光缆的其中一端传递到另需要花费 t单位时间 。现在 Freda要 进行 Q次试验, 每次选取两座房屋,并想知道 传呼机的信号在这两座房屋之间传递 至少需 要多长时间。 Freda 和 rainbow简直弱爆了有木有T_TT_T ,请你帮他们吧……
N座房屋 通过光缆 一定是连通的, 并且这 M条光缆有以下三类连接情况:
A:光缆不形成环 ,也就是光缆仅 有 N-1条。
B:光缆只 形成一个环,也就是光缆 仅有 N条。
C:每条光缆仅在一个环中。
Input
第一行 包含三个用空格隔开的整数, N、M和 Q。
接下来 M行每三个整数 x、y、t,表示 房屋 x和 y之间有一条传递时为 t的光缆 。
最后 Q行每两个整数 x、y,表示 Freda想知道 在 x和 y之间传呼最少需要多长时间。
Output
输出 Q行,每一个整数表示 Freda每次试验的结果 。
Sample Input
Input1:
5 4 2
1 2 1
1 3 1
2 4 1
2 5 1
3 5
2 1
Input2:
5 5 2
1 2 1
2 1 1
1 3 1
2 4 1
2 5 1
3 5
2 1
Input3:
9 10 2
1 2 1
1 4 1
3 4 1
2 3 1
3 7 1
7 8 2
7 9 2
1 5 3
1 6 4
5 6 1
1 9
5 7
Sample Output
Output1:
3
1
Output2:
3
1
Output3:
5
6
Data Constraint
- 送分数据占10%,2<=N<=1000,N-1<=M<=1200。
- A类数据占30%,M=N-1。
- B类数据占50%,M=N。
- C类数据占10%,M>N。
- 对于100%的数据,2<=N<=10000,N-1<=M<=12000,Q=10000,1<=x,y<=N,1<=t<=32768。
题目大意
给你一个图,图上每一条边最多只能在一个环中,给出Q组询问,每组询问是x,y,答x到y的最短距离
解题思路
业界毒瘤?仙人掌?不懂,总之讲一讲我的方法吧:
方法就是lca,可以将每个环看作一个节点,也就是说,除了环顶之外,其余环中的看作一点,那么我们就可以求出fa[x] (环顶或者父亲节点)和 di[x] (这个点到父亲节点的距离或到环顶的两条距离中,最小值)
- 我们可以首先用dfs找fa,di,标记光缆编号。
- 然后再一次dfs找出节点所在的层
- 最后lca一遍。
Codes
const
maxn=19231; maxx=83721;
var dis,s,p,first,last,f,owner,floor:array[0..10000]of longint;
fa,di:array[0..10000,0..16]of longint;
hush:array[0..maxn,1..3]of longint;
n,m,i,x,y,t,q,tot,j,top,z:longint;
a:array[0..24000,1..4]of longint;
bz,pd:array[0..12000]of boolean;
stack:array[0..10000,1..2]of longint;
function ha(x,y:longint):longint; //哈希求每条光缆的编号
var z:longint;
begin
z:=((x*maxx)+(y*y*2-1))mod maxn;
while (hush[z,3]<>0)and((hush[z,1]<>x)or(hush[z,2]<>y)) do z:=z mod maxn+1;
exit(z);
end;
procedure insert(w,x,y,z:longint); //插入边集数组
begin
a[w,1]:=x; a[w,2]:=y; a[w,3]:=z; a[w,4]:=w;
if first[x]=0 then first[x]:=w else a[last[x],4]:=w;
last[x]:=w;
end;
function turn(x:longint):longint;
begin
if x>m then x:=x-m;
exit(x);
end;
function min(p,q:longint):longint;
begin
if p<q then exit(p) else exit(q);
end;
procedure build(x,v,n,last:longint); //找环,做fa,di
var o,y,t:longint;
begin
if f[x]<>0 then //找到一个环
begin
inc(tot);
dis[tot]:=v-stack[f[x],2];
for o:=f[x]+1 to n-1 do
begin
y:=stack[o,1];
pd[y]:=true;
s[y]:=stack[o,2]-stack[f[x],2];
p[y]:=dis[tot]-s[y];
fa[y,0]:=x; di[y,0]:=min(s[y],p[y]);
owner[y]:=tot; //一个点的归属环
end;
end else
begin
f[x]:=n; //将每个节点存入栈中
stack[n,1]:=x;
stack[n,2]:=v;
o:=first[x];
while 1=1 do
begin
t:=turn(o);
if bz[t] then //判断该边是否走过
begin
if o=a[o,4] then break;
o:=a[o,4];
continue;
end;
y:=a[o,2];
bz[t]:=true;
build(y,v+a[o,3],n+1,x);
if a[o,4]=o then break;
o:=a[o,4];
end;
if not pd[x] then //处理不是环的情况
begin
inc(tot);
owner[x]:=tot;
fa[x,0]:=last;
di[x,0]:=stack[n,2]-stack[n-1,2];
end;
end;
end;
procedure find(x,t:longint); //求每个点的层数
var o,y:longint;
begin
o:=first[x];
floor[x]:=t;
bz[x]:=true;
while 1=1 do
begin
y:=a[o,2];
if bz[y] then
begin
if a[o,4]=o then break;
o:=a[o,4];
continue;
end;
if owner[y]<>owner[x] then find(y,t+1) else find(y,t);
if a[o,4]=o then break;
o:=a[o,4];
end;
end;
function min(x,y,z:longint):longint;
begin
if x>y then x:=y;
if x>z then x:=z;
exit(x);
end;
procedure swap(var a,b:longint);
var c:longint;
begin
c:=a; a:=b; b:=c;
end;
function ask(x,y:longint):longint; //求两点的最短路径
var dis,o:longint;
begin
if floor[x]>floor[y] then swap(x,y);
dis:=0;
for o:=16 downto 0 do
if 1<<o<=floor[y]-floor[x] then
begin
inc(dis,di[y,o]);
y:=fa[y,o];
end;
for o:=16 downto 0 do
begin
if fa[x,o]<>fa[y,o] then
begin
inc(dis,di[x,o]+di[y,o]);
x:=fa[x,o]; y:=fa[y,o];
end;
end;
if owner[x]=owner[y] then exit(dis+min(abs(s[x]-s[y]),s[x]+p[y],p[x]+s[y])) else exit(dis+di[x,0]+di[y,0]);
end;
begin
assign(input,'pager.in'); reset(input);
assign(output,'pager.out'); rewrite(output);
read(n,m,q);
for i:=1 to m do
begin
read(x,y,z);
if x>y then swap(x,y);
t:=ha(x,y);
if hush[t,3]<>0 then //将两点之间的所有直接到达的路求最小值
begin
t:=hush[t,3];
a[t,3]:=min(a[t,3],z);
a[t+m,3]:=min(a[t+m,3],z);
end else
begin
inc(top);
hush[t,1]:=x; hush[t,2]:=y; hush[t,3]:=top;
insert(top,x,y,z);
insert(top+m,y,x,z);
end;
end;
build(1,0,1,0);
for j:=1 to 16 do //搞lca
for i:=1 to n do
begin
fa[i,j]:=fa[fa[i,j-1],j-1];
di[i,j]:=di[i,j-1]+di[fa[i,j-1],j-1];
end;
fillchar(bz,sizeof(bz),false);
find(1,1);
for i:=1 to q do
begin
read(x,y);
writeln(ask(x,y));
end;
close(output); close(input);
end.