[poj3114]Countries in War

传送门

http://poj.org/problem?id=3114

题目大意

给出n个城市,m条边,如果从A能到B,从B也能到A,他们的通信时间为0. 求从s到t最短的通信时间。

题解

先求scc,然后在scc上跑最短路
我都换成离线处理了……无限TLE中QAQAQ

var
 dist,dfn,low,p,id:array[0..505]of longint;
 t:array[0..20000]of longint;
 z,w:array[0..5000,1..3]of longint;
 x:array[0..20000,1..4]of longint;
 i,j,k:longint;
 n,m,len,scc,a,b,c,q,tt,head,tail,v:longint;
function min(a,b:longint):longint;
begin
 if a>b then exit(b) else exit(a);
end;

procedure initz(a,b,c:longint);
begin
 z[len,1]:=b; z[len,2]:=c;
 if z[a,3]=0
 then z[a,3]:=len else z[z[a,1],3]:=len;
 z[a,1]:=len; inc(len);
end;

procedure initw(a,b,c:longint);
begin
 w[len,1]:=b; w[len,2]:=c;
 if w[a,3]=0
 then w[a,3]:=len else w[w[a,1],3]:=len;
 w[a,1]:=len; inc(len);
end;

procedure sort(l,r,vv:longint);
var i,j,a,b:longint;
begin
 i:=l; j:=r; a:=x[(l+r) div 2,vv];
 repeat
  while x[i,vv]<a do inc(i);
  while a<x[j,vv] do dec(j);
  if not(i>j) then
   begin
    b:=x[i,1]; x[i,1]:=x[j,1]; x[j,1]:=b;
    b:=x[i,2]; x[i,2]:=x[j,2]; x[j,2]:=b;
    b:=x[i,3]; x[i,3]:=x[j,3]; x[j,3]:=b;
    b:=x[i,4]; x[i,4]:=x[j,4]; x[j,4]:=b;
    inc(i); dec(j);
   end;
 until i>j;
 if l<j then sort(l,j,vv);
 if i<r then sort(i,r,vv);
end;

procedure spfa(a:longint);
var i:longint;
begin
 for i:=1 to scc do
  dist[i]:=1000000;
 dist[a]:=0; head:=1; tail:=2; t[1]:=a; p[a]:=1;
 while head<tail do
  begin
   v:=t[head]; inc(head); tt:=w[v,3];
   while tt<>0 do
    begin
     if dist[v]+w[tt,2]<dist[w[tt,1]] then begin
      dist[w[tt,1]]:=dist[v]+w[tt,2];
      if p[w[tt,1]]=0 then begin p[w[tt,1]]:=1; t[tail]:=w[tt,1]; inc(tail); end;
     end;
     tt:=w[tt,3];
    end;
   p[v]:=0;
  end;
end;

procedure tarjan(a:longint);
var tt:longint;
begin
 inc(len); dfn[a]:=len; low[a]:=len; inc(t[0]); t[t[0]]:=a; p[a]:=1;
 tt:=z[a,3];
 while tt<>0 do
  begin
   if dfn[z[tt,1]]=0
   then begin tarjan(z[tt,1]); low[a]:=min(low[z[tt,1]],low[a]); end
   else if p[z[tt,1]]=1 then low[a]:=min(dfn[z[tt,1]],low[a]);
   tt:=z[tt,3];
  end;
 if dfn[a]=low[a] then begin
  inc(scc);
  repeat
   p[t[t[0]]]:=0;
   id[t[t[0]]]:=scc;
   dec(t[0]);
  until a=t[t[0]+1];
 end;
end;

begin
 while not eof do
  begin
   readln(n,m); len:=n+1; if (n=0)and(m=0) then break;
   for i:=1 to m do
    begin readln(a,b,c); initz(a,b,c); end;
   z[0,1]:=len; len:=0; scc:=0; t[0]:=0;
   for i:=1 to n do
    if dfn[i]=0 then tarjan(i);
   len:=scc+1;
   for i:=1 to n do
    begin
     tt:=z[i,3];
     while tt<>0 do
      begin
       if id[i]<>id[z[tt,1]] then initw(id[i],id[z[tt,1]],z[tt,2]);
       tt:=z[tt,3];
      end;
    end;
   readln(q);
   for i:=1 to q do
    begin readln(a,b); x[i,1]:=id[a]; x[i,2]:=id[b]; x[i,3]:=i; end;
   sort(1,q,1); {x[i,1]}
   x[0,1]:=0;
   for i:=1 to q do
    begin
     if x[i,1]<>x[i-1,1] then spfa(x[i,1]);
     x[i,4]:=dist[x[i,2]];
    end;
   sort(1,q,3); {x[i,3]}
   for i:=1 to q do
    if x[i,4]=1000000
    then writeln('Nao e possivel entregar a carta')
    else writeln(x[i,4]);

   writeln;
   for i:=1 to n do
    for j:=1 to 3 do
     w[i,j]:=0;
   for i:=1 to n do
    for j:=1 to 3 do
     z[i,j]:=0;
   for i:=1 to n do
    begin dfn[i]:=0; low[i]:=0; end;
  end;
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值