jzoj3348. 【NOI2013模拟】秘密任务
Description
Input
输入文件为secret.in :
第一行 包含一 个正整数 T,表示有 T组测试数据。接下来 依次是 T组测试数 据。
每组测试数 据的第一行包含两个整N、M。
第二行包含 N - 1个正整数,依次表示 A1,A2, …,AN-1。
接下来 M行,每 行为三个 整数: ui、vi、ci,表示一条连接城市ui和城市 vi的路程等于 ci的高速公路。
Output
输出文件为 secret.out 。
输出 T行, 依次 表示 每组测试数据 的答案 。若最优 方案 唯一 则输出 ”Yes”和 最小 代价 ,否则 输出 ”No ”和最小 代价 。字符串 和整数 之间 请用一个 空格 隔开 。
Sample Input
3
3 3
2 4
1 3 23
3 2 12
2 1 11
4 4
3 2 2
1 2 1
2 3 1
3 4 1
4 1 1
3 4
3 2
1 2 1
2 3 2
2 3 19
3 1 4
Sample Output
Yes 4
Yes 3
No 2
【样例解释】
第 1组测试数据: 最优 方案 是在城市 1设立 两个 检查点 。
第 2组测试数据: 最优 方案 是城市 1的高速公路 (1, 4 )的出入口设立 检查点。
第 3组测试数据: 最优 方案 是在城市 2设立 一个 检查点 ,不过 既可以 设置 在 高速公路 (1, 2)的出入 口,也可以 设置 在高速公路 (2, 3)的出入口 。
Data Constraint
对于 10% 的数据: 2 ≤ N ≤ 10 , 1 ≤ M ≤ 20。
另有 40% 的数据: 最优 方案 是唯一 的。
对于 10 0% 的数据: 2 ≤ N ≤ 400, 1 ≤ M ≤ 4 00 0,1 ≤ T ≤ 5,1 ≤ Ai, c ≤ 10^9。无向图可能 有重边 。
题解
我们来发掘发掘题目的条件。
我们发现,frank只能走最短路。
so
我们考虑把最短路图建出来。
怎么建?
先跑一边spfa,求出dis。
然后枚举每一条边,x和y。如果\(dis[x]+边的代价=dis[y]\)则证明这条边是最短路图的边。
跑两边bfs即可。
然后我们发现,每次设立一个检查点就是把一条边给标记成不可通过。
那么这不就是把多条边给割掉使得1与n不连通吗?
最小割!
有因为最小割=最大流
我们在最短路图上跑最大流即可。
问题来了,如何判断只有一种方案呢?
其实这就是一个最小割的唯一性的问题。
有一个很好的解决方案——
我们在残余网络中求出s能够到达的点的集合(容量为0的边不可走)
然后再从t跑一边,记录下t的集合。
如果一条边的两个端点分别是在s集合和t集合中。
则表示当前的边是必割边。
证明显然。
然后如果所有的必割边原来的容量加起来等于最大流,则证明只有一种方案。
证明?
其实一张图即可解决。
我们发现,上面这种情况中间的两条容量为0的边可以任意割1条。
还要注意:
由于这题的最小割唯一性判断有些奇妙。
因为是一条边的两个端点可以断。
所以把每条边拆成两条边即可。
标程
调到自闭
var
i,j,k,l,n,m,x,y,z,flow,ll,tot,ans,t,head,tail,took,gs:longint;
dis,va,a:array[1..4000] of int64;
map,ttt,da:array[1..3000,1..3000] of int64;
bz,ss,tt:array[1..4000] of boolean;
pd,hav:array[1..3000,1..3000] of boolean;
gap,gapv:array[0..5001] of int64;
tov,next,last,cap:array[1..100000] of int64;
flag:boolean;
function min(x,y:longint):longint;
begin
if x>y then exit(y);exit(x);
end;
procedure insert(x,y,z:longint);
begin
inc(tot);tov[tot]:=y;next[tot]:=last[x];last[x]:=tot;cap[tot]:=z;
inc(tot);tov[tot]:=x;next[tot]:=last[y];last[y]:=tot;cap[tot]:=0;
end;
procedure dg(u:longint);
var
mingap,llt,i,now:longint;
begin
mingap:=n-1;
llt:=ll;
if u=n then
begin
inc(flow,ll);
flag:=true;
exit;
end;
i:=last[u];
while i>0 do
begin
if cap[i]>0 then
begin
if gap[tov[i]]+1=gap[u] then
begin
if ll>cap[i] then ll:=cap[i];
dg(tov[i]);
if gap[1]>=n then exit;
if flag then
begin
now:=i;
break;
end;
ll:=llt;
end;
if gap[tov[i]]<mingap then mingap:=gap[tov[i]];
end;
i:=next[i];
end;
if not flag then
begin
dec(gapv[gap[u]]);
if gapv[gap[u]]=0 then gap[1]:=n;
gap[u]:=mingap+1;
inc(gapv[gap[u]]);
end
else
begin
dec(cap[now],ll);
inc(cap[now xor 1],ll);
end;
end;
procedure spfa(dep:longint);
var
i,j:longint;
begin
for i:=1 to n do
begin
if map[a[dep],i]>0 then
begin
if dis[i]>map[a[dep],i]+dis[a[dep]] then
begin
dis[i]:=map[a[dep],i]+dis[a[dep]];
if bz[i] then
begin
bz[i]:=false;
inc(took);
a[took]:=i;
end;
end;
end;
end;
bz[a[dep]]:=true;
end;
procedure bfs(dep:longint);
var
i,j:longint;
begin
for i:=1 to n do
begin
if map[a[dep],i]>0 then
begin
if bz[i] then
begin
bz[i]:=false;
inc(took);
a[took]:=i;
end;
if dis[i]=dis[a[dep]]+map[a[dep],i] then
begin
pd[a[dep],i]:=true;
end;
end;
end;
end;
procedure find1(dep:longint);
var
i,j:longint;
begin
i:=last[a[dep]];
while i>0 do
begin
if bz[tov[i]] then
begin
if cap[i]>0 then
begin
bz[tov[i]]:=false;
ss[tov[i]]:=true;
inc(took);
a[took]:=tov[i];
end;
end;
i:=next[i];
end;
end;
procedure find2(dep:longint);
var
i,j:longint;
begin
i:=last[a[dep]];
while i>0 do
begin
if bz[tov[i]] then
begin
if cap[i xor 1]>0 then
begin
bz[tov[i]]:=false;
tt[tov[i]]:=true;
inc(took);
a[took]:=tov[i];
end;
end;
i:=next[i];
end;
end;
begin
//assign(input,'0data.in');reset(input);
readln(t);
while t>0 do
begin
dec(t);
readln(n,m);
for i:=1 to n-1 do read(va[i]);
va[n]:=maxlongint;
fillchar(map,sizeof(map),0);
fillchar(ttt,sizeof(ttt),0);
for i:=1 to m do
begin
readln(x,y,z);
if (x=4) and (y=6) then
j:=j;
if map[x,y]>0 then
begin
if map[x,y]>z then
begin
ttt[x,y]:=1;
map[x,y]:=z;
end
else
if map[x,y]=z then inc(ttt[x,y]);
end
else
begin
map[x,y]:=z;
ttt[x,y]:=1;
end;
map[y,x]:=map[x,y];
ttt[y,x]:=ttt[x,y];
end;
head:=1;tail:=1;took:=1;
fillchar(bz,sizeof(bz),true);
bz[1]:=false;
a[1]:=1;
fillchar(dis,sizeof(dis),127);
dis[1]:=0;
repeat
for i:=head to tail do spfa(i);
head:=tail+1;tail:=took;
until head>tail;
fillchar(pd,sizeof(pd),false);
head:=1;tail:=1;took:=1;
fillchar(bz,sizeof(bz),true);
bz[1]:=false;
a[1]:=1;
repeat
for i:=head to tail do bfs(i);
head:=tail+1;tail:=took;
until head>tail;
tot:=1;fillchar(last,sizeof(last),0);
gs:=0;
k:=0;
for i:=1 to n-1 do
for j:=1 to n do
if pd[i,j] then
inc(gs,ttt[i,j]);
fillchar(da,sizeof(da),0);
for i:=1 to n-1 do
for j:=1 to n do
if pd[i,j] then
begin
for l:=1 to ttt[i,j] do
begin
inc(k);
hav[i,k+n-1]:=true;
da[i,k+n-1]:=va[i];
insert(i,k+n-1,va[i]);
if j=n then
begin
da[k+n-1,n+gs]:=va[j];
insert(k+n-1,n+gs,va[j]);
hav[k+n-1,n+gs]:=true;
end
else
begin
da[k+n-1,j]:=va[j];
insert(k+n-1,j,va[j]);
hav[k+n-1,j]:=true;
end;
end;
end;
n:=n+gs;
fillchar(gap,sizeof(gap),0);
fillchar(gapv,sizeof(gapv),0);
gapv[0]:=n;
flow:=0;
while gap[1]<n do
begin
ll:=maxlongint;
flag:=false;
dg(1);
end;
k:=0;
head:=1;tail:=1;took:=1;
fillchar(bz,sizeof(bz),true);
bz[1]:=false;
a[1]:=1;
fillchar(ss,sizeof(ss),false);
ss[1]:=true;
repeat
for i:=head to tail do find1(i);
head:=tail+1;tail:=took;
until head>tail;
head:=1;tail:=1;took:=1;
fillchar(bz,sizeof(bz),true);
bz[n]:=false;
a[1]:=n;
fillchar(tt,sizeof(tt),false);
tt[n]:=true;
repeat
for i:=head to tail do find2(i);
head:=tail+1;tail:=took;
until head>tail;
for i:=1 to n do
begin
for j:=1 to n do
begin
if hav[i,j] then
begin
if (ss[i]) and (tt[j]) then
begin
inc(k,da[i,j]);
end;
end;
end;
end;
if k<>flow then write('No ') else write('Yes ');
writeln(flow);
end;
end.
end.