手抄代码 + 学习指针 + 左思右想一夜终于——在一瞬间开窍了。果真题目都是这样:突破了一个点,一切都是柳暗花明。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; }