手抄代码 + 学习指针 + 冥思苦想一晚上终于——在一瞬间开窍了。果然题目都是这样:突破了一个点,一切都是柳暗花明。
题面描述:
样例:
这道题目,首先注意到给定的边的性质:这些边在平面上构成了一棵树,区间之间互不相交,只有包含与外离两种关系。如果不考虑颜色的限制,我们将原图的边权转化为网络流中的流量,那么原图中的最短路就转化为了新图中的最小割。那么在张网络流的图上,我们应当如何限制颜色的制约关系呢?
首先一个明显的思路:这张图是一个树形的结构,画在一个类似数轴的东西上面会很容易发现最下面的一条链上的点是无论如何都要经过的;而不存在于这条链上的点,则一定不会被访问到,其所代表的颜色也一定不会被我们所选择。对于这样的点,我们将它们从我们的图上删去。最小割:将图中的点分做S割与T割的两个部分。我们对于每一个颜色都做出一个辅助点,若这个点位于S割,代表这个颜色被选择;位于T割,代表不被选择。
我们将所有的边画成树后,从所有的大区间层层推进的向其所包含的小区间连边(类似线段树)。注意在这里我们先忽略那些链接相邻两点的区间不作处理。一个显然的性质:一个大区间所跳过的点,一定包含了所有它包含的小区间跳过的点。那么我们就从区间往它跳过的颜色的点连上INF的边(如果大区间&小区间共同跳过了一个颜色,这条边从小区间->颜色)。注意之前我们确定一定不会经过的颜色,从它向T点连INF的边,保证它一定处于T割。
这样我们可以发现:如果不选择这一个点,说明我们的割线一定在这个颜色的点的上方->我们选择了所有跳过这个颜色的区间。如果选择一个点,说明我们的割线在这个点的下方->我们没有选择任何一个跳过这个颜色的区间。这样,限制就得以满足了。最后,那些链接相邻两点的边:如果包含于大区间,则由这些区间其中最小的一个向T点连边权值的流量的边,否则就从S连向T,流量也为边权值。
感觉读懂了之后除了感叹还是感叹——我学过网络流吗?不存在的。【摊手】
#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; }