jzoj2702. 探險&jzoj3917. 【NOIP2014模擬11.2A組】福慧雙修
Description
探險家小T好高興!X國要舉辦一次溶洞探險比賽,獲獎者將得到豐厚獎品哦!小T雖然對獎品不感興趣,但是這個大振名聲的機會當然不能錯過!
比賽即將開始,工作人員說明了這次比賽的規則:每個溶洞和其他某些溶洞有暗道相連。兩個溶洞之間可能有多條道路,也有可能沒有,但沒有一條暗道直接從自己連到自己。參賽者需要統一從一個大溶洞出發,並再次回到這個大溶洞。
如果就這麼點限制,那麼問題就太簡單了,可是舉辦方又提出了一個條件:不能經過同一條暗道兩次。這個條件讓大家犯難了。這該怎麼辦呢?
到了大溶洞口後,小T愉悅地發現這個地方他曾經來過,他還記得有哪些暗道,以及通過每條暗道的時間。小T現在向你求助,你能幫他算出至少要多少時間才能回到大溶洞嗎?
Input
第一行兩個數n,m表示溶洞的數量以及暗道的數量。
接下來m行,每行4個數s、t、w、v,表示一個暗道連線的兩個溶洞s、t,這條暗道正著走(s à t)的所需要的時間w,倒著走(t à s)所需要的時間v。由於溶洞的相對位置不同,w與v可能不同。
Output
輸出一行一個數t,表示最少所需要的時間。
Sample Input
3 3
1 2 2 1
2 3 4 5
3 1 3 2
Sample Output
8
Data Constraint
對於30%的資料,n<=500,m<=10000
對於60%的資料,n<=2000,m<=50000
對於100%的資料,n<=10000,m<=200000,1<=w,v<=10000
注:3917. 【NOIP2014模擬11.2A組】福慧雙修
這題與上面的探險題意完全一樣,但"福慧雙修"沒有重邊而"探險"有重邊。
題解
恩,我們先看到30%到60%的做法。
然後發現就算是白痴也會打著部分分。
直接上暴力即可。
那麼對於100%有3種做法——
一、
我們考慮IDA*。
具體做法就是先在外面二分一個答案值,然後利用IDA*判斷可否做。
然後,我們可以每次以1到x號點的距離作為距離標號。
然後剪枝即可(時間複雜度玄學)
當然,能過,然而福慧雙修的資料過大,這似乎不夠保險。
二、
考慮DP。
我們先在圖的右邊新建一個節點1’那麼就從1開始走。
我們設一個狀態f[i,j]表示1第一個到i這個狀態,再走到j這個距離1’的狀態的最短路。
那麼就可以dp了。
當然,這個會超空間。
但是前者顯然不用,用一個東西來標記j從哪裡來即可。
三、
考慮重構圖+dij
你可否聽過“關於spfa:他死了”嗎?
我們可以先跑一遍dij,求出dist,然後我們還要求出一個prep[i]表示從1出發到i這條最短路徑中,連線1第一個出去的點。
假設下面這張圖:
那麼prep就為:0,2,3,2,3,2,2
既然我們知道了這個prep那麼求來試試構建新的圖。
列舉所有的邊,起點為u,終點為v,邊長為z。
- 1、當u=1,v<>1時
- 若 prep[v]=v,即說明原點到達點 v 的最短路徑即為1→v,故此時不再新增邊(dist[v] 已代表該邊)
- 若 prev[v]<>v,說明原點到達點 v 的最短路徑不是1→v,此時需要在新圖中新增邊(1,v,w)
- 2、當u<>1,v=1時
- 若 u<>prev[u] 說明從原點到達點 u 的最短路徑中沒有經過邊1→u,即邊u→1可以被使用,此時存在一條原點 1→prep[u]→…→u→1的路徑
- 在新圖中直接建立一條 1→答案(n+1)長為dist[u]+z的邊
- 若 u=prep[u] 說明到達點 u 的最短路徑是由邊1→u得到,所以不能通過dist[u]+z的方式返回原點。
- 但如果存在其他方式到達點 u,則可以通過該邊返回,故在新圖中建立邊u→答案長為z的邊。
- 3、當u<>1,v<>1時
- 若prep[u]<>prep[v]
- 建1→v長為dist[u]+z的邊(為什麼?因為這表示可以由1→u→v→1)
- 反之建u→v長為z的邊(為什麼?因為已存在在第一次dij中)
如此看來,再建跑一次dij即可。
但是邊會有重複。那麼我們可以發現,如果重複的邊不是1→x,那麼不會影響。
但是1→x重複了,就有鍋了。
實際上直接在一開始時特判一下即可。
其實如果運氣好,走到順序不同,也可以過掉。
我就是那種運氣好的。
程式
uses math;
var
bz:array[1..400000] of boolean;
g,dist,wz,d,tov,last,next,v,prep,x,y,z1,z2:array[0..400000] of longint;
tov1,last1,next1,v1,x1,y1,z3:array[0..400000] of longint;
i,j,k,l,n,m,maxx,t,temp,tot,tot1,now,many:longint;
procedure insert1(x,y,z:longint);
begin
inc(tot1);
tov1[tot1]:=y;
next1[tot1]:=last1[x];
last1[x]:=tot1;
v1[tot1]:=z;
end;
procedure insert(x,y,z:longint);
begin
inc(tot);
tov[tot]:=y;
next[tot]:=last[x];
last[x]:=tot;
v[tot]:=z;
end;
procedure up(x:longint);
var
temp:longint;
begin
while (x div 2>0) and (dist[d[x]]<dist[d[x div 2]]) do
begin
wz[d[x]]:=x div 2;
wz[d[x div 2]]:=x;
temp:=d[x];
d[x]:=d[x div 2];
d[x div 2]:=temp;
x:=x div 2;
end;
end;
procedure down(y:longint);
var
temp,x:longint;
begin
x:=1;
while ((x*2<=t) and (dist[d[x]]>dist[d[x*2]])) or ((x*2+1<=t) and (dist[d[x]]>dist[d[x*2+1]])) do
begin
if (x*2+1<=t) and (dist[d[x*2+1]]<dist[d[x*2]]) then
begin
wz[d[x]]:=x*2+1;
wz[d[x*2+1]]:=x;
temp:=d[x];
d[x]:=d[x*2+1];
d[x*2+1]:=temp;
x:=x*2+1;
end
else
begin
wz[d[x]]:=x*2;
wz[d[x*2]]:=x;
temp:=d[x];
d[x]:=d[x*2];
d[x*2]:=temp;
x:=x*2;
end;
end;
end;
begin
assign(input,'0data.in');reset(input);
readln(n,m);
for i:=1 to m do
begin
readln(x[i],y[i],z1[i],z2[i]);
insert(x[i],y[i],z1[i]);
insert(y[i],x[i],z2[i]);
inc(many);
x1[many]:=x[i];
y1[many]:=y[i];
z3[many]:=z1[i];
inc(many);
x1[many]:=y[i];
y1[many]:=x[i];
z3[many]:=z2[i];
end;
fillchar(dist,sizeof(dist),127 div 3);
maxx:=dist[1];
t:=1;
dist[1]:=0;
bz[1]:=true;
wz[1]:=t;
d[t]:=1;
up(t);
while t>0 do
begin
bz[d[1]]:=true;
i:=last[d[1]];
while i>0 do
begin
if (bz[tov[i]]=false) and (dist[d[1]]+v[i]<dist[tov[i]]) then
begin
if dist[tov[i]]=maxx then
begin
inc(t);
dist[tov[i]]:=dist[d[1]]+v[i];
wz[tov[i]]:=t;
d[t]:=tov[i];
if d[1]=1 then
begin
prep[tov[i]]:=tov[i];
end
else
prep[tov[i]]:=prep[d[1]];
up(t);
end
else
begin
dist[tov[i]]:=dist[d[1]]+v[i];
prep[tov[i]]:=prep[d[1]];
up(wz[tov[i]]);
end;
end;
i:=next[i];
end;
wz[d[1]]:=0;
wz[d[t]]:=1;
d[1]:=d[t];
dec(t);
down(t);
end;
now:=1;
for i:=1 to many do
begin
if (x1[i]=1) and (y1[i]<>1) then
begin
if prep[y1[i]]<>y1[i] then
begin
insert1(x1[i],y1[i],z3[i]);
end;
end
else
if (x1[i]<>1) and (y1[i]=1) then
begin
if x1[i]<>prep[x1[i]] then
begin
insert1(1,n+1,dist[x1[i]]+z3[i]);
end
else
begin
insert1(x1[i],n+1,z3[i]);
end;
end
else
if (x1[i]<>1) and (y1[i]<>1) then
begin
if prep[x1[i]]<>prep[y1[i]] then
begin
insert1(1,y1[i],dist[x1[i]]+z3[i]);
end
else
begin
insert1(x1[i],y1[i],z3[i]);
end;
end;
end;
fillchar(bz,sizeof(bz),0);
fillchar(dist,sizeof(dist),127 div 3);
fillchar(d,sizeof(d),0);
t:=1;
dist[1]:=0;
bz[1]:=true;
wz[1]:=t;
d[t]:=1;
up(t);
while t>0 do
begin
bz[d[1]]:=true;
i:=last1[d[1]];
while i>0 do
begin
if (bz[tov1[i]]=false) and (dist[d[1]]+v1[i]<dist[tov1[i]]) then
begin
if dist[tov1[i]]=maxx then
begin
inc(t);
dist[tov1[i]]:=dist[d[1]]+v1[i];
wz[tov1[i]]:=t;
d[t]:=tov1[i];
up(t);
end
else
begin
dist[tov1[i]]:=dist[d[1]]+v1[i];
up(wz[tov1[i]]);
end;
end;
i:=next1[i];
end;
wz[d[1]]:=0;
wz[d[t]]:=1;
d[1]:=d[t];
dec(t);
down(t);
end;
writeln(dist[n+1]);
end.