bzoj 2407 最短路

题意:从1出发再回到1,每条边能且只能经过一次(对于一条边 u->v 和 v->u 的时间可能是不同的但算作同一条边),求最少的时间

显然,如果我们不要求回来的话,随便跑跑最短路即可,因为我们是绝对不会经过重边的,即绝对不会走过去再走回来,这样的话我完全可以不走它。所以当你确定了第一步,即从1出发到哪个和1直接相连的点x,删去走过去的边,跑x到1的最短路即可

但是同样显然的是,枚举+最短路 你会T的很惨。。。

orz    hzwer:http://hzwer.com/5227.html

以下是蒟蒻口胡(只是为了自己捯一下),正确、理性、简洁的分析请见上面hzwer的传送门

我们会发现,当我们计算与1直接相连的点x到1的满足限制的最短路的时候,会有大量重复的计算,因为无论是哪个点作为第一步,只要它能走到一点y,那么一定会顺着y到1在不经过重边的最短路走回来,那么这段路就可能被多个点重复计算,即同一条满足条件的路径可能会被多次计算、很多点也会被重复经过

考虑建一个新图,使其跑最短路的时候,去掉重复的计算

我们设置一个虚拟终点T,最终ans=dis'[T];

我们用(x,y,z)表示原图x->y 边权为z 的边;

我们先计算出1->每个点的最短路dis[i]

令fir[i]表示从1到i的最短路上经过的第一个点,即从1到i的最短路是第一步到fir[i]走来的;

(1) 当x=1时

①如果 fir[y]=y ,则说明y就是我们选择的第一步,即dis[y]=z;那么以后一定会有点是通过这条边走过去的(至少是它自己),所以这条边完全没有建的必要

②如果 fir[y]<>y ,则说明(1,y,z)不存在我们原本的最短路上,在新图上连(1,y,z)

(2)当y=1时

①如果fir[x]=x , 则说明x的最短路是由1->x走出的,路径是1->x->1,是不能通过dis[x]+z走回1(经过2次了)的,但如果其他点走到x是可以通过x返回的,所以连(x,T,z)

②如果fir[x]<>x,则说明x的最短路是由其他点转移来的,即存在路径 1->fir[x]->...->x->1 ,所以直接连(1,T,dis[x]+z)

(3)当 x<>1 && y<>1 时

①如果fir[x]=fir[y], 则说明存在路径 1->fir[x]->..->x->y->..->1,保留(x,y,z)

②如果fir[x]<>fir[y],,则说明存在路径1->fir[x]->..->x->...->1,1->fir[y]->..->y->...->1,且第一条路径是可以通过x到y在走回1的,那么直接连(1,y,dis[x]+z)

最后在新图中重跑一遍最短路即可

var
        n,m,l,size,x,y,z:longint;
        t               :longint;
        i               :longint;
        last,last2      :array[0..10010] of longint;
        pre2,other2,len2:array[0..400010] of longint;
        pre,other,len   :array[0..400010] of longint;
        heap,pos,fir    :array[0..10010] of longint;
        dis             :array[0..10010] of int64;

procedure swap(var a,b:Longint);
var
        c:longint;
begin
   c:=a; a:=b; b:=c;
end;

procedure connect(x,y,z:longint);
begin
   inc(l);
   pre[l]:=last[x];
   last[x]:=l;
   other[l]:=y;
   len[l]:=z;
end;

procedure connect2(x,y,z:longint);
begin
   inc(l);
   pre2[l]:=last2[x];
   last2[x]:=l;
   other2[l]:=y;
   len2[l]:=z;
end;

procedure heap_up(i:longint);
begin
   if i=1 then exit;
   while i>1 do
   begin
      if dis[heap[i]]>1]] then
      begin
         swap(heap[i],heap[i>>1]);
         pos[heap[i]]:=i;
         pos[heap[i>>1]]:=i>>1;
         i:=i>>1;
      end else exit;
   end;
end;

procedure heap_down(i:longint);
var
        t:longint;
begin
   while i<<1<=size do
   begin
      if dis[heap[i]]dis[heap[(i<<1)+1]] then t:=(i<<1)+1;
      if i<>t then
      begin
         swap(heap[i],heap[t]);
         pos[heap[i]]:=i;
         pos[heap[t]]:=t;
         i:=t;
      end else exit;
   end;
end;

procedure dijkstra;
var
        p,q,cur,i:longint;
begin
   for i:=1 to t do dis[i]:=maxlongint*100;
   dis[1]:=0;
   for i:=1 to t do
   begin
      cur:=heap[1];
      heap[1]:=heap[size];
      dec(size);
      heap_down(1);
      //
      q:=last[cur];
      while (q<>0) do
      begin
         p:=other[q];
         if dis[p]>dis[cur]+len[q] then
         begin
            dis[p]:=dis[cur]+len[q];
            if cur=1 then fir[p]:=p else fir[p]:=fir[cur];
            heap_up(pos[p]);
         end;
         q:=pre[q];
      end;
   end;
end;

procedure dijkstra2;
var
        p,q,cur,i:longint;
begin
   for i:=1 to t do dis[i]:=maxlongint*100;
   dis[1]:=0;
   for i:=1 to t do
   begin
      cur:=heap[1];
      heap[1]:=heap[size];
      dec(size);
      heap_down(1);
      //
      q:=last2[cur];
      while (q<>0) do
      begin
         p:=other2[q];
         if dis[p]>dis[cur]+len2[q] then
         begin
            dis[p]:=dis[cur]+len2[q];
            heap_up(pos[p]);
         end;
         q:=pre2[q];
      end;
   end;
end;

procedure rebuild;
var
        i,p,q:longint;
begin
   inc(t); l:=0;
   for i:=1 to n do
   begin
      q:=last[i];
      while (q<>0) do
      begin
         p:=other[q];
           if (i=1) and (fir[p]<>p) then connect2(1,p,len[q]) else
               if (p=1) and (fir[i]<>i) then connect2(1,t,dis[i]+len[q]) else
                if (p=1) and (fir[i]=i) then connect2(i,t,len[q]) else
                  if (i<>1) and (p<>1) and (fir[p]=fir[i]) then connect2(i,p,len[q]) else
                    if (i<>1) and (p<>1) and (fir[p]<>fir[i]) then connect2(1,p,dis[i]+len[q]);
         q:=pre[q];
      end;
   end;
   for i:=1 to t do heap[i]:=i;
   for i:=1 to t do pos[i]:=i;
   size:=t;
end;

begin
   read(n,m);  t:=n;
   for i:=1 to m do
   begin
      read(x,y,z);
      connect(x,y,z);
      read(z);
      connect(y,x,z);
   end;
   for i:=1 to n do heap[i]:=i;
   for i:=1 to n do pos[i]:=i;
   size:=n;
   dijkstra;
   rebuild;
   dijkstra2;
   writeln(dis[t]);
end.
——by Eirlys



你可能感兴趣的:(bzoj,最短路)