bzoj 1922: [Sdoi2010]大陆争霸 带限制最短路

题意:有n个点和m条道路。有一些点必须经过了某些点后才能到达,问从起点到达终点的最短时间。


分析:带限制的最短路。

设d1[x]为可以进入点x的时间,d2[x]为到达点x的时间,s[x]为点x被多少个点保护。

一开始以为d1和d2是有关联的,也就是要通过d1来推出某些d2,然后想破脑袋都想不出(其实也没那么夸张啦)。后来看了题解才发现这两个可以分开求,然后进入点x的实际时间为max(d1[x],d2[x])。

然后跑dijkstra,每次找到一个d2最小且s[x]=0的点,然后更新与这个点相连的点和被这个点保护的城市dec(s).


代码:

const
  maxn=3007;
  maxm=70007;

var
  n,m,e:longint;
  last,s,last1:array[1..maxn] of longint;
  d1,d2:array[1..maxn] of int64;
  side:array[1..maxm*15] of record
    x,y,z,next:longint;
  end;
  v:array[1..maxn] of boolean;

procedure add(x,y,z:longint);
begin
  inc(e);
  side[e].x:=x; side[e].y:=y; side[e].z:=z;
  side[e].next:=last[x]; last[x]:=e;
end;

procedure add1(x,y:longint);
begin
  inc(e);
  side[e].x:=x; side[e].y:=y;
  side[e].next:=last1[x]; last1[x]:=e;
end;

procedure init;
var
  x,y,z,i,j:longint;
begin
  readln(n,m);
  for i:=1 to m do
  begin
    readln(x,y,z);
    add(x,y,z);
  end;
  for i:=1 to n do
  begin
    read(s[i]);
    for j:=1 to s[i] do
    begin
      read(y);
      add1(y,i);
    end;
    if s[i]>0 then d1[i]:=1 shl 62;
  end;
end;

function max(x,y:int64):int64;
begin
  if x>y then exit(x)
         else exit(y);
end;

procedure dij;
var
  u,i:longint;
  min,w:int64;
begin
  for i:=2 to n do
    d2[i]:=1 shl 62;
  fillchar(v,sizeof(v),true);
  repeat
    min:=1 shl 62;
    u:=0;
    for i:=1 to n do
      if (v[i])and(d2[i]<min)and(s[i]=0) then
      begin
        min:=d2[i];
        u:=i;
      end;
    if u>0 then
    begin
      w:=max(d1[u],d2[u]);
      v[u]:=false;
      i:=last[u];
      while i>0 do
        with side[i] do
        begin
          if (w+z<d2[y])and(v[y]) then d2[y]:=w+z;
          i:=next;
        end;
      i:=last1[u];
      while i>0 do
        with side[i] do
        begin
          dec(s[y]);
          if s[y]=0 then d1[y]:=w;
          i:=next;
        end;
    end;
  until u=0;
  writeln(max(d1[n],d2[n]));
end;

begin
  init;
  dij;
end.


你可能感兴趣的:(bzoj 1922: [Sdoi2010]大陆争霸 带限制最短路)