手抄代碼 + 學習指針 + 左思右想一夜終於——在一瞬間開竅了。果真題目都是這樣:突破了一個點,一切都是柳暗花明。node
題面描述:c++
樣例:網絡
這道題目,首先注意到給定的邊的性質:這些邊在平面上構成了一棵樹,區間之間互不相交,只有包含與外離兩種關係。若是不考慮顏色的限制,咱們將原圖的邊權轉化爲網絡流中的流量,那麼原圖中的最短路就轉化爲了新圖中的最小割。那麼在張網絡流的圖上,咱們應當如何限制顏色的制約關係呢?學習
首先一個明顯的思路:這張圖是一個樹形的結構,畫在一個相似數軸的東西上面會很容易發現最下面的一條鏈上的點是不管如何都要通過的;而不存在於這條鏈上的點,則必定不會被訪問到,其所表明的顏色也必定不會被咱們所選擇。對於這樣的點,咱們將它們從咱們的圖上刪去。最小割:將圖中的點分作S割與T割的兩個部分。咱們對於每個顏色都作出一個輔助點,若這個點位於S割,表明這個顏色被選擇;位於T割,表明不被選擇。spa
咱們將全部的邊畫成樹後,從全部的大區間層層推動的向其所包含的小區間連邊(相似線段樹)。注意在這裏咱們先忽略那些連接相鄰兩點的區間不做處理。一個顯然的性質:一個大區間所跳過的點,必定包含了全部它包含的小區間跳過的點。那麼咱們就從區間往它跳過的顏色的點連上INF的邊(若是大區間&小區間共同跳過了一個顏色,這條邊從小區間->顏色)。注意以前咱們肯定必定不會通過的顏色,從它向T點連INF的邊,保證它必定處於T割。指針
這樣咱們能夠發現:若是不選擇這一個點,說明咱們的割線必定在這個顏色的點的上方->咱們選擇了全部跳過這個顏色的區間。若是選擇一個點,說明咱們的割線在這個點的下方->咱們沒有選擇任何一個跳過這個顏色的區間。這樣,限制就得以知足了。最後,那些連接相鄰兩點的邊:若是包含於大區間,則由這些區間其中最小的一個向T點連邊權值的流量的邊,不然就從S連向T,流量也爲邊權值。code
感受讀懂了以後除了感嘆仍是感嘆——我學過網絡流嗎?不存在的。【攤手】blog
#include <bits/stdc++.h> using namespace std; #define maxn 10000 #define INF 99999 #define pb push_back #define vec vector int n, m, cnp, cnt, s, t; int Map[maxn][maxn], lev[maxn], nxt[maxn]; int ans, a[maxn], id[maxn]; bool tag[maxn], mark[maxn], flag[maxn]; vector <int> u, v, w, c; queue <int> q; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } struct node { int u, v, w; bool flag; bool operator <(node t) { return v - u < t.v - t.u; } // 區間長度短的放在前面 }E[maxn]; struct edge { int v, f; edge *nxt, *rev; }e[100000], *p = e, *head[maxn], *cur[maxn]; void add(int u, int v, int f1, int f2) { *p = (edge) { v, f1, head[u], p + 1 }, head[u] = p ++; *p = (edge) { u, f2, head[v], p - 1 }, head[v] = p ++; } bool bfs() { memset(lev, 0, sizeof(lev)); q.push(s); lev[s] = 1; while(!q.empty()) { int u = q.front(); q.pop(); for(edge *i = head[u]; i; i = i -> nxt) { if(!lev[i -> v] && i -> f) { lev[i -> v] = lev[u] + 1; q.push(i -> v); } } } return lev[t]; } int dfs(int x, int nf) { int ff = 0; if(x == t) return nf; for(edge *i = cur[x]; i; i = i -> nxt) { if(!nf) break; if(i -> f && lev[i -> v] == lev[x] + 1) { int af = dfs(i -> v, min(nf, i -> f)); i -> f -= af, i -> rev -> f += af, cur[x] = i; ff += af, nf -= af; } } cur[x] = head[x]; return ff; } int Work(vec <int> u, vec <int> v, vec <int> w, vec <int> c) { memset(Map, 80, sizeof(Map)); for(int i = 0; i < (int) u.size(); i ++) Map[u[i]][v[i]] = Map[v[i]][u[i]] = min(Map[u[i]][v[i]], w[i]); flag[n] = 1; for(int i = n - 1; i; i --) for(int j = i + 1; j <= n; j ++) flag[i] |= flag[j] && Map[i][j] < 1 << 30; for(int i = 0; i <= n; i = nxt[i]) { a[id[i] = ++ cnt] = i; nxt[i] = n + 1; for(int j = i + 1; j <= n; j ++) if(Map[i][j] < 1 << 30 && nxt[i] > n && flag[j]) { nxt[i] = j; break; } for(int j = 0; j < i; j ++) if(Map[i][j] < 1 << 30 && id[j] && id[j] != cnt - 1) E[++ cnp] = (node) { id[j], cnt, Map[i][j], 0 }; } if(a[cnt] != n) return -1; E[++ cnp] = (node) { id[0], id[n], 0, 0 }; sort(E + 1, E + cnp); s = cnp, t = cnp + 1001; for(int i = 1; i <= n - 1; i ++) if(!id[i]) add(cnp + c[i - 1], t, 1 << 30, 1 << 30); for(int i = 1; i <= cnp; i ++) { for(int j = 1; j < i; j ++) if(!E[j].flag && E[i].u <= E[j].u && E[i].v >= E[j].v) E[j].flag = 1, add(i, j, E[j].w, 1 << 30); for(int j = E[i].u + 1; j < E[i].v; j ++) if(!tag[j]) tag[j] = 1, add(i, cnp + c[a[j] - 1], 1 << 30, 1 << 30); for(int j = E[i].u; j < E[i].v; j ++) if(!mark[j]) mark[j] = 1, add(i, t, Map[a[j]][a[j + 1]], 0); } for(int i = 1; i <= t; i ++) cur[i] = head[i]; while(bfs()) if((ans += dfs(s, 1 << 30)) >= INF) return -1; return ans; } int main() { n = read(), m = read(); for(int i = 1; i <= n - 1; i ++) { int x = read(); c.pb(x); } for(int i = 1; i <= m; i ++) { int x = read(), y = read(), z = read(); u.pb(x), v.pb(y), w.pb(z); } printf("%d\n", Work(u, v, w, c)); return 0; }