【APIO2009】ATM

[Apio2009]Atm

Time Limit:15000MS Memory Limit:165536K
Total Submit:4 Accepted:4
Case Time Limit:1500MS

Description

1179.jpg

Input

第一行包含两个整数N、M。N表示路口的个数,M表示道路条数。接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道 路的起点和终点的路口编号。接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数。接下来一行包含两个整数S、P,S表示市中心的编号,也 就是出发的路口。P表示酒吧数目。接下来的一行中有P个整数,表示P个有酒吧的路口的编号

Output

输出一个整数,表示Banditji从市中心开始到某个酒吧结束所能抢劫的最多的现金总数。

Sample Input

6 7
1 2
2 3
3 5
2 4
4 1
2 6
6 5
10
12
8
16
1 5
1 4
4
3
5
6

Sample Output

47

Hint

50%的输入保证N, M<=3000。所有的输入保证N, M<=500000。每个ATM机中可取的钱数为一个非负整数且不超过4000。输入数据保证你可以从市中心沿着Siruseri的单向的道路到达其中的至少一个酒吧。

  方法:Tarjan缩点之后SPFA求最长路搞定。

  Q:SPFA不会出环挂掉么?

  A:都Tarjan了,哪里还有环……直接变成一棵树了……

  

  Q:为什么我在OJ上交会WA或者TLE……

  A:当时比赛貌似开8M系统栈,OJ无法给开这么大的……本机测试(fpc 2.4.2)每组数据都可以在0.4s内出解。

  附:APIO数据下载     http://www.apio.olympiad.org/2009/

Program Apio_2009_ATM;
type rec=record
               endv,next:longint;
end;

var edge,map:Array[1..500000]of rec;
    father2,dist,order,atm,money,v,dfn,low,stack,father:Array[1..500000]of longint;
    Instack,vis:array[1..500000]of boolean;
    q:Array[0..10000000]of longint;
    n,m,s,p,total,tot,index,module,ans:longint;

function min(a,b:longint):longint;
begin
  if a<b then exit(a);
  exit(b);
end;

procedure addedge(s,t:longint);
begin
  inc(total);
  edge[total].endv:=t;
  edge[total].next:=father[s];
  father[s]:=total;
end;

procedure addedge2(s,t:longint);
begin
  inc(total);
  map[total].endv:=t;
  map[total].next:=father2[s];
  father2[s]:=total;
end;

procedure init;
var i,a,b:longint;
begin
  fillchar(father,sizeof(father),$ff);
  readln(n,m);
  for i:=1 to m do
    begin
      readln(a,b);
      addedge(a,b);
    end;
  for i:=1 to n do readln(money[i]);
  readln(s,p);
  for i:=1 to p do read(atm[i]);
end;

procedure tarjan(x:longint);
var p:longint;
begin
  inc(index);
  dfn[x]:=index;
  low[x]:=index;
  Instack[x]:=true;
  inc(tot);
  stack[tot]:=x;
  p:=father[x];
  while p>0 do
    begin
      if dfn[edge[p].endv]=0 then
        begin
          tarjan(edge[p].endv);
          low[x]:=min(low[x],low[edge[p].endv]);
        end
      else if instack[edge[p].endv] then
        low[x]:=min(low[x],dfn[edge[p].endv]);
      p:=edge[p].next;
    end;
  if dfn[x]=low[x] then
    begin
      inc(module);
      repeat
        order[stack[tot]]:=module;
        Instack[stack[tot]]:=false;
        dec(tot);
      until stack[tot+1]=x;
    end;
end;

procedure BuildNewGraph;
var i,p:longint;
begin
  fillchar(father2,sizeof(father2),$ff);
  for i:=1 to n do inc(v[order[i]],money[i]);
  total:=0;
  for i:=1 to n do
    begin
      p:=father[i];
      while p>0 do
        begin
          if order[i]<>order[edge[p].endv] then   addedge2(order[i],order[edge[p].endv]);
          p:=edge[p].next;
        end;
    end;
end;

procedure compress;
var i:longint;
begin
  fillchar(dfn,sizeof(dfn),0);
  fillchar(low,sizeof(low),0);
  fillchar(Instack,sizeof(Instack),false);
  tot:=0;index:=0;
  for i:=1 to n do
    if dfn[i]=0 then Tarjan(i);
  BuildNewGraph;
end;

procedure spfa(s:longint);
var head,tail,p,now:longint;
begin
  fillchar(vis,sizeof(vis),false);
  head:=0;tail:=0;
  q[head]:=s;vis[s]:=true;
  dist[s]:=v[s];
  while head<=tail do
    begin
      now:=q[head];
      p:=father2[now];
      while p>0 do
        begin
          if dist[map[p].endv]<dist[now]+v[map[p].endv] then
            begin
              dist[map[p].endv]:=dist[now]+v[map[p].endv];
              if not vis[map[p].endv] then
                begin
                  vis[map[p].endv]:=true;
                  tail:=(tail+1)mod 10000000;
                  q[tail]:=map[p].endv;
                end;
            end;
          p:=map[p].next;
        end;
      head:=(head+1)mod 10000000;
      vis[now]:=false;
    end;
end;

procedure Print;
var i:longint;
begin
  for i:=1 to p do
    if dist[order[atm[i]]]>ans then ans:=dist[order[atm[i]]];
  writeln(ans);
end;

begin
  init;
  Compress;
  spfa(order[s]);
  Print;
end.

转载于:https://www.cnblogs.com/Delostik/archive/2011/03/22/1991850.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值