这题本身没什么技术性,但是这个数据太强了。。。
说下我的思路:
step1.求强连通分量,缩点,消圈,用O(N+M)的Tarjan算法显然是最佳选择,但是要注意到:如果用递归Tarjan会爆栈的,所以要手动写个栈(想想就胃疼)。
step2.求最大权值路径:
法一.spfa之,复杂度O(KE),但是实践证明这数据居然把spfa常数卡住了(或许是我缩点过程耗时大了,反正超了一个点)。
法二.拓扑排序+图上DP,复杂度O(N+M),满分。
==================================================================
贴代码(7kb):
program APIO09AIM;
const MaxN=500000;
type link=^node; node=record x:longint; back,next:link; end;//back为反向边指针
var Gz,Gf:array[1..MaxN] of link;//图分了双向边存储,便于combine操作实现
Low,Dfn,Weight,Dist,Stack,Du,Ar:array[1..MaxN] of longint;
isVisit,inStack,isEnd:array[1..MaxN] of boolean;
TarStack:Array[1..MaxN] of record i:longint; p:link; end;
N,S,Ans,Top,Index,TarTop:longint;
//Simple
procedure Swap(var i,j:longint);
var tmp:longint;
begin tmp:=i; i:=j; j:=tmp;
end;
function Min(a,b:longint):longint;
begin if a<b then exit(a) else exit(b);
end;
function Max(a,b:longint):longint;
begin if a>b then exit(a) else exit(b);
end;
//End of Simple
//Stack
procedure Push(i:longint);
begin inc(Top); Stack[Top]:=i;
inStack[i]:=True;
end;
function Pop:longint;
begin pop:=Stack[Top]; dec(Top);
inStack[Pop]:=false;
end;
//End of Stack
//Graph
procedure Conn(i,j:longint);//(i->j);
var pz,pf:link;
begin
new(pz); new(pf);
with pz^ do begin x:=j; next:=Gz[i]; back:=pf; end; Gz[i]:=pz;
with pf^ do begin x:=i; next:=Gf[j]; back:=pz; end; Gf[j]:=pf;
end;
procedure Combine(i,j:longint);//合并节点操作,注意可能存在边<i,i>
var p:link;
begin if j=S then Swap(i,j);
//writeln('Combine(',i,',',j,');');
while Gz[j]<>nil do begin
p:=Gz[j]; Gz[j]:=Gz[j]^.next;
p^.back^.x:=i;
p^.next:=Gz[i]; Gz[i]:=p;
end;
while Gf[j]<>nil do begin
p:=Gf[j]; Gf[j]:=Gf[j]^.next;
p^.back^.x:=i;
p^.next:=Gf[i]; Gf[i]:=p;
end;
inc(Weight[i],Weight[j]);
isEnd[i]:=isEnd[i] or isEnd[j];
end;
//End of Graph
//Init
procedure Init;
var M,P,i,a,b:longint;
begin
readln(N,M);
for i:=1 to M do begin
readln(a,b);
Conn(a,b)
end;
for i:=1 to N do readln(Weight[i]);
readln(S,P);
fillchar(isEnd,sizeof(isEnd),false);
for i:=1 to P do begin
read(a);
isEnd[a]:=true;
end;
end;
//End of Init;
//ConTract(DG)//递归版本的Tarjan算法
procedure Tarjan_DG(i:longint);
var p:link; j:longint;
begin isVisit[i]:=True;
inc(Index); Low[i]:=Index; Dfn[i]:=Index;
Push(i);
p:=Gz[i];
while p<>nil do begin
if not isVisit[p^.x] then begin
Tarjan_DG(p^.x);
Low[i]:=Min(Low[i],Low[p^.x]);
end else if inStack[p^.x] then
Low[i]:=Min(Low[i],Low[p^.x]);
p:=p^.next;
end;
if Low[i]=Dfn[i] then
repeat
j:=Pop;
if i<>j then Combine(i,j);
until j=i;
end;
procedure ConTract_DG;//消圈,与递归版本的Tarjan配合
begin
Index:=0; Top:=0;
fillchar(isVisit,sizeof(isVisit),false);
fillchar(inStack,sizeof(inStack),false);
Tarjan_DG(S);
end;
//End of ConTract(DG)
//Contract(UDG)//手动栈的Tarjan
procedure ConTract_UDG;
var i,j,res:longint;
p:Link;
begin
Fillchar(isVisit,sizeof(isVisit),false); isVisit[S]:=True;
fillchar(inStack,sizeof(inStack),false); Top:=0; Push(S);
TarTop:=1; TarStack[1].i:=S;
TarStack[1].p:=Gz[S];
Low[S]:=1; Dfn[S]:=1; Index:=1; res:=-1;
while TarTop>0 do begin
i:=TarStack[TarTop].i;
p:=TarStack[TarTop].p;
if p=nil then begin
if Low[i]=Dfn[i] then
repeat
j:=Pop;
if j<>i then Combine(i,j);
until j=i;
dec(TarTop);
res:=1;
end else if not isVisit[p^.x] then begin
inc(TarTop);
TarStack[TarTop].i:=p^.x;
isVisit[p^.x]:=True;
inc(Index);
Low[TarStack[TarTop].i]:=Index;
Dfn[TarStack[TarTop].i]:=Index;
Push(p^.x);
TarStack[TarTop].p:=Gz[p^.x];
res:=-1;
end else begin
if res=1 then Low[i]:=Min(Low[i],Low[p^.x])
else if inStack[p^.x] then Low[i]:=Min(Low[i],Low[p^.x]);
TarStack[TarTop].p:=TarStack[TarTop].p^.next;
res:=-1;
end;
end;
end;
//End of ConTract(UDG)
//Spfa(tle)//spfa法求最大权值路径,Tle了一个点
procedure spfa;//S->>
var p:link; x:longint;
begin
Ans:=0;
fillchar(Dist,Sizeof(Dist),0);
fillchar(inStack,sizeof(inStack),false);
Top:=0; push(S);
Dist[S]:=Weight[S];
while Top>0 do begin
x:=Pop;
if isEnd[x] then Ans:=Max(Ans,Dist[x]);
p:=Gz[x];
while p<>nil do begin
if p^.x<>x then
if Dist[x]+Weight[p^.x]>Dist[p^.x] then begin
Dist[p^.x]:=Dist[x]+Weight[p^.x];
if not inStack[p^.x] then Push(p^.x);
end;
p:=p^.next;
end;
end;
end;
//End Spfa
//TpSort;//拓扑排序
procedure TpSort;
var i,Count:longint; p:link;
begin
Top:=0;
for i:=1 to N do begin
Du[i]:=0;
p:=Gz[i]; while p<>nil do begin
if p^.x<>i then inc(Du[i]);
p:=p^.next;
end;
if Du[i]=0 then Push(i);
end;
Count:=0;
while Top>0 do begin
i:=Pop;
p:=Gf[i]; while p<>nil do begin
if p^.x<>i then begin
dec(Du[p^.x]);
if Du[p^.x]=0 then Push(p^.x);
end;
p:=p^.next;
end;
inc(Count); Ar[Count]:=i; //writeln('TpSort(',i,');');
end;
end;
//
//Dp//拓扑排序后图上DP
procedure Dp;
var i,x:longint; p:link;
begin
Ans:=0;
for i:=N downto 1 do begin
x:=Ar[i];
Dist[x]:=Weight[x];
p:=Gf[x]; while p<>nil do begin
if p^.x<>x then Dist[x]:=Max(Dist[x],Weight[x]+Dist[p^.x]);
p:=p^.next;
end;
if isEnd[x] then Ans:=Max(Ans,Dist[x]);
end;
end;
//End of Ep
//Main//主函数
procedure Main;
begin
Init;
ConTract_UDG;
TpSort;Dp;{spfa is tle}
write(Ans);
end;
//End of Main
//Print;//Debug部分,请忽略
procedure print;
var i:longint; p:link;
begin for i:=1 to N do begin
p:=Gz[i]; while p<>nil do begin
writeln(i,'>>',p^.x);
p:=p^.next;
end;
end;
for i:=1 to N do writeln('Weight[',i,']=',weight[i]);
end;
//End of print
begin
Assign(input,'atm.in'); reset(input);
Assign(output,'atm.out'); rewrite(output);
Main; //Print;
close(input); close(output);
end.
==================================================================这题真的不难,只不过手动开栈很闹心啊!!