bzoj 4283 魔法少女伊莉雅 - 最短路
題目傳送門
需要高級權限的快速通道
題目大意
給定一個$n$個點$m$條邊的帶正權無向圖。要求找一條路徑滿足:
- 它是一條簡單路徑
- 它是一條嚴格次短路
- 對於任何一條可能存在於最短路上的邊,不能包含它的反向邊。
不存在這條路徑輸出-1。
神題orz...orz....orz....良心的最短路性質題,涵蓋了大部分最短路徑樹和最短路徑圖上的常用性質。
直覺是最短路上隨意判一判就好了。然後全WA了,qwq。。
然後開始講正題。
將$d\left(u, v \right )$記為點$u$到點$v$的最短路的長度。
記$d_{s}\left(x \right ) = d\left(s, x \right ), d_{t}\left(x \right ) = d\left(x, t \right )$。
最短路徑圖是$s$到$t$的所有最短路徑的並集。
舉個例子有助於說明:
左邊為原圖,右邊為最短路徑圖。
註意 最短路徑圖是一個有向圖。
這裏將原圖記為$G = (V, E)$,最短路徑圖記為$G* = (V*, E*)$。
最短路徑圖的基本性質I(定義1.1) 對於任意$e \in E*$,若$e = (u, v)$,那麽$d_{s}(u) + w(e) + d_{t}(v) = d(s, t)$。
顯然最短路徑圖一定是一個DAG。
然後繼續討論最短路徑圖上的性質。
最短路徑圖的基本性質II(定理1.1) 對於任意$x \in V*$,那麽有$d_{s}(x) + d_{t}(x) = d(s, t)$。
證明 如果$s = x$,那麽結論顯然成立。
現在考慮$s \neq x$的情況。暫時記$L = d(s, t)$
由最短路徑圖的定義可知$d_{s}(x) \geqslant L - d_{t}(x)$。因為$x$不是起點,所以必然存在一個前驅$x‘$。
根據基本性質I有$d_{s}(x‘) + w(x‘, x) + d_{t}(x) = L$。由$d_{s}(x)$的定義可知$d_{s}(x) \leqslant d_{s}(x‘) + w(x, x‘) = L - d_{t}(x)$。
所以$d_{s}(x) = L - d_{t}(x)$。
因此定理得證。
最短路徑圖的基本性質III(推論1.2) 對於$e = (x, y) \in E*$,那麽有$d_{s}(x) + w(x, y) = d_{s}(y)$。
證明 根據定理1.1有$d_{s}(y) = d(s, t) - d_{t}(y)$。根據定義1.1有$d_{s}(x) + w(x, y) = d(s, t) - d_{t}(y)$。然後定理得證。
最短路徑圖的基本性質IV(推論1.3) 如果最短路徑圖中存在一條$x$到$y$的簡單路徑,那麽$d_{s}(x) + l(P*(x, y)) + d_{t}(y) = L$。其中P*(x, y)表示一條在$G*$上的路徑,l(P*(x, y))表示這條路徑的長度。
重復使用推論1.2可以得到$d_{s}(x) + l(P*(x, y)) = d_{s}(y)$。然後根據定理1.1易證。詳細證明留給讀者。
最短路徑圖的基本性質V(推論1.4) 最短路徑圖中一條$x$到$y$的簡單路徑,對應原圖中一條$x$到$y$的最短路。
有了推論1.3就可以使用反證法。詳細證明留給讀者。同時可以推出上面的$ l(P*(x, y)) = d(x, y)$。
定理1.5 若$x, y \in V*$,且滿足$x \neq y, d_{s}(x) \leqslant d_{x}(y)$,那麽在$G*$中$s$到$x$的最短路與$y$到$t$的最短路不相交。
證明 根據最短路徑圖的定義(定義1.1)可知,$s$到$x$的過程中$d_{s}(x‘)$遞增,$y$到$t$的過程中$d_{s}(y‘)$遞增。又因為$x \neq y, d_{s}(x) \leqslant d_{x}(y)$,所以它們不相交。
然後來講一些約定吧。
正向邊:對於一條有向邊$(u, v)$,它在$E*$中,那麽我們稱它為一條正向邊。
反向邊:對於一條有向邊$(u, v)$,如果$(v, u) \in E*$,那麽我們稱它為一條反向邊。
內部邊:正向邊和反向邊統稱為內部邊。
外部邊:在$E$中,但不屬於$E*$的邊。
現在來明確一下約定路徑的符號。
$P(u, v)$,表示一條$u$到$v$的路徑。
$P*(u, v)$,表示一條在$G*$中$u$到$v$的路徑。
$P(x, y) + Q(y, z)$,表示一條沿著路徑$P$從$x$走到$y$的,然後沿著$Q$,從$y$走到$z$的路徑。
$P^{-1}(x, y)$,表示沿著路徑$P$的反向邊(不是上面的定義的反向邊),從$y$到$x$的一條路徑。
$P^{0}(x‘, y‘)$,路徑$P$上,一條$x‘$到$y‘$的子路徑。
$l(P)$,表示路徑$P$的長度。
設所求路徑為$S$。
外部路徑:起點和終點在$G*$中,經過的變都是外部邊的一條簡單路徑。稱起點是這條路徑的拐出點,終點是這條路徑的回歸點。
定理2.1 路徑$S$中存在至少1條外部路徑。
證明 如果不包含外部路徑,那麽路徑$S$中的邊都是正向邊(因為不能走反向邊)。由於$G*$是一個DAG,所以$l(S) = d(s, t)$,不符合題目要求。
定理2.2 路徑$S$的包含一條外部路徑的拐出點為$x$,回歸點為$y$,那麽有$d_{s}(x) \leqslant d_{s}(y)$。
證明 假設結論不成立,那麽有$d_{s}(x) > d_{s}(y)$$。
設$S = P(s, x) + Q(x, y) + R(y, t)$。那麽令$U = P*(s, y) + Q^{-1}(x, y) + R*(x, t)$。因為路徑$Q$是一條簡單路徑,P*和R*都是由正向邊組成,根據定理1..5可得P*與R*不相交。所以$U$是一條簡單路徑。
又因為$U$包含了至少一條外部邊,所以它不是最短路。因此是一條滿足題目要求的路徑。
又因為$P*(s, y) < P*(s, x) \leqslant P(s, x), R*(x, t) < R*(y, t) \leqslant R(y, t), Q(x, y) = Q^{-1}(x, y)$,所以$l(U) < l(S)$。
與$S$的最優性矛盾。
定理2.3 路徑$S$恰好包含一條外部路徑。
證明 假設包含的外部路徑數目不是一條。
- 如果不包含外部路徑,顯然矛盾。
- 如果包含超過一條外部路徑,設$S = P*(s, x) + Q(x, y) + R(y, t)$,令$U = P*(s, x) + Q(x, y) + R*(y, t)$,其中$Q(x, y)$是一條外部路徑。
根據定理1.5易證$U$是一條簡單路徑,根據最短路徑圖的定義有$R*(y, t) < R(y, t)$,因此$l(U) < l(S)$,與$S$的最優性矛盾。
然後約定$S$的拐出點為$S$包含的外部路徑的拐出點,它的回歸點為它包含的外部路徑的回歸點。
接下來看一看有關最短路徑樹的性質。
定義3.1 定義一棵以$p$為根的最短路徑樹$T_{p} = (V, E_{T})$是原圖中以$p$為根的一棵有向路徑生成樹。其中一條邊$e(v, u)$滿足$d(u, p) + w(e) = d(v, p)$。
由於下面只會用到$T_{t}$,因此,以下可能會直接將它簡記$T$。
註意
- 這裏的最短路徑樹是一個有向圖。
- 所有有向邊都指向根節點。
- 一張無向圖的最短路徑圖唯一,但指定點的最短路徑生成樹可能不唯一。
然後再來定義定義子樹。
定義3.2 在以$p$為根的最短路徑生成樹中:
點$x$的子樹,在$T_{p}$點$x$斷掉點$x$的唯一一條出邊後,剩下的以$x$為根的樹是點$x$的子樹。記為$T_{p}(x)$。
點$x$的真子樹,點$x$的真子樹是$T_{p}(x)$的一個子圖。在$T_{p}(x)$中,存在於點$x$的真子樹的點,當且僅當它到$x$的路徑上不經過除了$x$以外的任何屬於$G*$的點。也就是說一個不是$x$的點,但屬於$G*$,一定不存在於$x$的真子樹中。記為$T*_{p}(x)$。
真子樹的定義可能不是很好理解(再加上我語文不好),那麽來舉個栗子:
在第三幅圖之後,邊權都被省略。在第四幅圖和第五幅圖中間橙色點標出的是在$G*$中的點。
下面有一個關於真子樹的很基本的性質。
定理3.1 對於任意$x,y \in G*$,都有$T*(x)\cap T*(y) = \varnothing $。
根據真子樹的定義易證。
接下來再來約定一個記號。
$P_{T}(x, y)$,在樹$T_{t}$中,一條$x$到$y$的路徑。
最短路徑樹中的基本性質(定理3.2) 在最短路徑樹$T_{t}$中,任意一個點$x$到其祖先$y$的一條簡單路徑,對應原圖中一條$x$到$y$的最短路。
證明 易證當$y = t$時結論成立。
考慮$y \neq t$的情況。
仍然假設不是最短路。那麽存在一條更優的路徑的從$x$到$y$,然後到$t$的路徑。與$T_{t}$的定義矛盾。
因此定理得證。
註意到如果將這條路徑反向,可以對應$y$到$x$在原圖中的一條最短路。
定義3.3 在$T$中,$x$的真祖先是在路徑$P_{T}(x, t)$中,離$x$最近的一個在$G*$中的點。將它記作$prt(x)$。
換一個說法就是沿著$x$向它的出邊走,直到遇到一個在$G*$中的點。註意,它可能是$x$也可能是$t$。
設$S$的拐出點為$x$,回歸點為$z$,顯然$x \in T*(x), z \in T*(z)$,根據定理3.1,那麽$T*(x)$和$T*(z)$不存在交集。所以在$S$上必然存在一條外部邊$(w, w‘)$使得$w \in T*(x)$,且$S^{0}(w‘, z)$中的各點均不在$T*(x)$中。有一個很顯然的事實是$prt(w) = x$。
定理3.3 $d_{s}(x) \leqslant d_{s}(prt(w‘))$。
註意 以下證明非常繁瑣,請先喝口水再繼續閱讀。
證明 仍然假設結論不成立。那麽有$d_{s}(x) > d_{s}(prt(w‘))$。所以$d_{t}(x) < d_{t}(prt(w‘))$。根據定理2.2有$d_{s}(x) <= d_{s}(z)$,因此$d_{s}(prt(w‘)) < d_{s}(z)$,所以$prt(w‘) \neq z$。根據定理3.1,可知,必然存在一條邊$(y, y‘)$使得$y \in T*(prt(w‘))$且$S^{0}(y‘, z)$中的各點均不在$T*(prt(w‘))$。顯然$prt(w‘) = prt(y)$。
令$Q = P*(s, prt(w‘)), R = P_{T}(prt(w‘), y), P = Q + R + S^{0}(y, t)$(見下圖)。
可以證明$R$實際上是在$T*(prt(w‘))$中。假設路徑上經過了其它的在$G*$中的點,那麽可知$prt(y)$不等於$prt(w‘)$,矛盾。
因為$d_{s}(prt(w‘))< d_{s}(z)$,根據定理1.5可知$Q$不與$S^{0}(z, t)$相交,又因為$R$在$T*(prt(w‘))$中,所以經過的邊都是外部邊,所以$P$中的內部邊不相交。又因為$S^{0}(y‘. z)$是一條外部的簡單路徑,且不在$T*(prt(w‘))$。所以路徑$P$中的外部邊不相交。因此路徑$P$是一條簡單路徑。(註意:這裏的相交指的是存在環,而不是邊與邊存在公共點。)
又因為$P$包含了至少一條外部邊,所以$P$是一條滿足要求的路徑(除了嚴格次短)。
因為$S$是所求路徑,所以有:
$l(S) \leqslant l(P)$
$l(S^{0}(s, x)) + l(S^{0}(x, y)) + l(S^{0}(y, t)) \leqslant l(Q) + l(R) + l(S^{0}(y, t))$
$l(S^{0}(s, x)) + l(S^{0}(x, y)) \leqslant l(Q) + l(R)$
$d_{s}(x) + l(S^{0}(x, y)) \leqslant d_{s}(prt(w‘)) + l(R)$(推論1.4)
又因為$prt(w‘) < d_{s}(x)$,所以$l(S^{0}(x, y)) < l(R)$。
令$U = S^{0}(x, y)^{-1} + P*(x, t)$。那麽有:
$l(U) = l(S^{0}(x, y)^{-1}) + l(P*(x, y))\\=l(S^{0}(x,y)) + d_{t}(x)\\<l(R) + d_{t}(prt(w‘))\\=l(R^{-1}) + d_{t}(prt(w‘))\\=l(P_{t}(y,t))$
但是$U$經過了至少一條外部邊,所以有$l(U) > l(P_{T}(y, t)) = d_{t}(y)$。但是剛剛卻推出了與之矛盾的式子。
所以假設不成立,定理得證:$d_{s}(x) \leqslant(prt(w‘))$。
這是最後一個定理:
定理3.4 必然存在一個滿足所有條件的路徑S*,滿足
$S* = P_{T}(s, w) + (w, w‘) + P_{T}(w‘, t)$
其中$(w, w‘)$是定理3.3中涉及到的一條邊。
證明 令$Q = P_{T}(s, w), R = P_{T}(w‘, t)$,根據定理3.1易證$S*$的外部邊不相交,根據定理3.3和定理1.5易證$S*$的內部邊不會相交。又因為$(w, w‘)$一定是一條外部邊。所以$S*$是一條簡單路徑但不是最短路徑。
因為$S$中包含至少一條非樹邊,然後用反證法易證存在一個$S*$是滿足題目所有要求的路徑(除非原問題不存在解)。
於是定理3.4創造了無限可能。
我們只需要枚舉一條非內部邊,非樹邊$e(x, y)$,且滿足$prt(x) \neq prt(y)$且$prt(x) \leqslant prt(y)$,然後用$d_{s}(x) + w(e) + d_{t}(y)$去更新答案即可。
雖然證明很復雜,但是算法卻異常簡單。
(終於可以完結撒花了,感覺像是寫了3小時+的論文)
Code
1 /** 2 * bzoj 3 * Problem#4283 4 * Accepted 5 * Time: 2720ms 6 * Memory: 31172k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 typedef class Edge { 13 public: 14 int ed, nx, w; 15 16 Edge(int ed = 0, int nx = 0, int w = 0):ed(ed), nx(nx), w(w) { } 17 }Edge; 18 19 typedef class MapManager { 20 public: 21 int ce; 22 int* h; 23 Edge* es; 24 25 MapManager() { } 26 MapManager(int n, int m):ce(-1) { 27 h = new int[(n + 1)]; 28 es = new Edge[(m + 1)]; 29 memset(h, -1, sizeof(int) * (n + 1)); 30 } 31 32 void addEdge(int u, int v, int w) { 33 es[++ce] = Edge(v, h[u], w); 34 h[u] = ce; 35 } 36 37 Edge& operator [] (int p) { 38 return es[p]; 39 } 40 }MapManager; 41 42 typedef class Node { 43 public: 44 int p; 45 int dis; 46 47 Node(int p = 0, int dis = 0):p(p), dis(dis) { } 48 49 boolean operator < (Node b) const { 50 return dis > b.dis; 51 } 52 }Node; 53 54 int n, m; 55 MapManager g; 56 MapManager rg; 57 int *prt; 58 int *f1, *f2; 59 boolean *flags; 60 61 inline void init() { 62 scanf("%d%d", &n, &m); 63 g = MapManager(n, m << 1); 64 rg = MapManager(n, m << 1); 65 for (int i = 1, u, v, w; i <= m; i++) { 66 scanf("%d%d%d", &u, &v, &w); 67 g.addEdge(u, v, w); 68 g.addEdge(v, u, w); 69 } 70 } 71 72 priority_queue<Node> que; 73 74 void dijstra(int s, int* &f) { 75 f = new int[(n + 1)]; 76 memset(f, 0x3f, sizeof(int) * (n + 1)); 77 que.push(Node(s, f[s] = 0)); 78 while (!que.empty()) { 79 Node e = que.top(); 80 que.pop(); 81 if (e.dis != f[e.p]) continue; 82 for (int i = g.h[e.p]; ~i; i = g[i].nx) { 83 Node eu (g[i].ed, f[e.p] + g[i].w); 84 if (eu.dis < f[eu.p]) { 85 f[eu.p] = eu.dis; 86 que.push(eu); 87 } 88 } 89 } 90 } 91 92 void dfs(int p) { 93 for (int i = rg.h[p]; ~i; i = rg[i].nx) { 94 int e = rg[i].ed; 95 if (f1[e] + f2[e] == f1[n]) 96 prt[e] = e; 97 else 98 prt[e] = prt[p]; 99 dfs(e); 100 } 101 } 102 103 int ans = (1 << 30); 104 inline void solve() { 105 dijstra(1, f1), dijstra(n, f2); 106 prt = new int[(n + 1)]; 107 flags = new boolean[(m * 2 + 1)]; 108 memset(prt, 0, sizeof(int) * (n + 1)); 109 for (int i = 1, e; i <= n; i++) { 110 for (int j = g.h[i]; ~j; j = g[j].nx) { 111 e = g[j].ed; 112 if (f2[e] + g[j].w == f2[i]) { 113 rg.addEdge(e, i, 0); 114 flags[j] = true; 115 break; 116 } 117 } 118 } 119 prt[n] = n; 120 dfs(n); 121 for (int i = 1, e; i <= n; i++) { 122 for (int j = g.h[i]; ~j; j = g[j].nx) { 123 e = g[j].ed; 124 if (!flags[j]) { 125 if (f1[i] + g[j].w + f2[e] == f1[n] || f2[i] + g[j].w + f1[e] == f1[n]) continue; 126 if (prt[i] != prt[e] && f1[prt[i]] <= f1[prt[e]]) 127 ans = min(ans, f1[i] + g[j].w + f2[e]); 128 } 129 } 130 } 131 if (ans == (1 << 30)) 132 puts("-1"); 133 else 134 printf("%d", ans); 135 } 136 137 int main() { 138 init(); 139 solve(); 140 return 0; 141 }
寫在後面的鬼扯
由於這篇博客寫了3個小時+,所以不能保證證明過程全是正確的,有問題請及時指出。
參考文獻
《excape解題報告》 by Unknown
很抱歉,我真的不知道它的作者是誰。只是當作考試的題解發下來的資料。
特別鳴謝
感謝YYR提供的資料以及許多騙分做法
感謝Doggu和我一起研究這個問題
感謝ZJC幫助我調試程序
(轉載請註明出處:http://www.cnblogs.com/yyf0309/p/8563071.html)
bzoj 4283 魔法少女伊莉雅 - 最短路