【BZOJ3691】游行 最小可相交路径覆盖转化
因为C是不断变化的而且C是和点权相关和边权无关 所以我们可以MCMF但是MCMF的时候不能与C相关
再分析问题 我们可以认为每条路径S->T只覆盖T这个终点 因为题目中说了如果Si != Ti 要多付出 C的代价
假设我们走过的路径形成了一个环则刚好 边数=点数 覆盖完了
如果走过的路径不是一个环 则还有起点没有覆盖 此时我们可以把它当作没有途径的城市 给他补偿 同样为C
所以我们把原图拆成左边出点 右边入点 传递闭包后建图
这样每次MCMF增广的代价是不递减的 并且每增广一次多覆盖一个点 所以我们把每次增广的代价放到一个数组里面
每次询问我们二分C在数组里的位置,在C之前的点我们利用路径覆盖 在C及C之后的点我们用C去补偿它
#include<bits/stdc++.h> using namespace std; #define ll long long #define MAX 255 inline int read() { int x = 0; bool t = false; char ch = getchar(); while ((ch < '0' || ch > '9') && ch != '-') ch = getchar(); if (ch == '-') t = true, ch = getchar(); while (ch <= '9' && ch >= '0') x = x * 10 + ch - 48, ch = getchar(); return t ? -x : x; } int val[MAX], sum[MAX], tot; namespace MCMF { const int MAXM = 1000000, MAXN = 1000; struct Line { int v, next, w, fy; } e[MAXM]; int h[MAXN], cnt = 2; inline void Add(int u, int v, int w, int fy) { e[cnt] = (Line) { v, h[u], w, fy }; h[u] = cnt++; e[cnt] = (Line) { u, h[v], 0, -fy }; h[v] = cnt++; } int dis[MAXN], pe[MAXN], pv[MAXN], Cost, Flow; bool vis[MAXN]; queue<int> Q; int S = 0, T = MAXN - 1; bool SPFA() { memset(dis, 63, sizeof(dis)); dis[S] = 0; Q.push(S); vis[S] = true; while (!Q.empty()) { int u = Q.front(); Q.pop(); for (int i = h[u]; i; i = e[i].next) { int v = e[i].v; if (!e[i].w) continue; if (dis[u] + e[i].fy < dis[v]) { dis[v] = dis[u] + e[i].fy; pe[v] = i, pv[v] = u; if (!vis[v]) vis[v] = true, Q.push(v); } } vis[u] = false; } if (dis[T] >= 1e9) return false; int flow = 1e9; for (int i = T; i != S; i = pv[i]) flow = min(flow, e[pe[i]].w); for (int i = T; i != S; i = pv[i]) e[pe[i]].w -= flow, e[pe[i] ^ 1].w += flow; Flow += flow; Cost += dis[T] * flow; val[++tot] = dis[T] * flow; sum[tot] = sum[tot - 1] + val[tot]; return true; } } using namespace MCMF; int n, m, q, g[MAX][MAX]; int main() { n = read(); m = read(); q = read(); memset(g, 63, sizeof(g)); for (int i = 1; i <= n; ++i) g[i][i] = 0; for (int i = 1, u, v; i <= m; ++i) u = read(), v = read(), g[u][v] = min(read(), g[u][v]); for (int k = 1; k <= n; ++k) for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) g[i][j] = min(g[i][j], g[i][k] + g[k][j]); for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) if (i ^ j) Add(i, j + n, 1, g[i][j]); for (int i = 1; i <= n; ++i) Add(S, i, 1, 0), Add(i + n, T, 1, 0); while (SPFA()); while (q--) { int C = read(), l = 1, r = tot, ret = 0; while (l <= r) { int mid = (l + r) >> 1; if (val[mid] < C) l = mid + 1, ret = mid; else r = mid - 1; } printf("%d\n", sum[ret] + (n - ret)*C); } return 0; }