[ARC176E] Max Vector
转化题意
你需要找出两个序列 \(x,y\),在满足下列条件的情况下最小化 \(\sum (x_i+y_i)\):
-
对于任意 \(1\leq i\leq N\),满足 \(x_i\geq X_i,y_i\geq Y_i\)
-
对于任意 \(1\leq i\leq M\),满足 \(\forall j, x_j\geq A_{i,j}\) 或者 \(\forall j, y_j\geq A_{i,j}\)
注:后面会把对于任意 \(i\) 的条件简写成 \(x\geq A_i\) 或 \(y\geq A_i\)。
解题思路
这题是最优化问题,具有无法贪心、限制与值域上大小有关、数据范围小的特点,我们使用网络流最小割解决。
考虑经典的拆点做法。我们对于每个 \(x_i\) 建出 \(500\) 个点,同时令 \(x_i\) 的第 \(j\) 个点像第 \(j+1\) 个点连一条对应值的边,表示选这个数。当 \(x_i=j\) 的时候就割掉这条边。\(y_i\) 的点要倒着建(为了后面第二个限制)。
对于上述题意的第一个限制,只要把不满足限制的边去掉就行了。
考虑怎么满足第二个限制,形式为“\(x\) 或 \(y\)”这样的限制在最小割里难以直接做。我们将条件转化为:当 \(x<A_i\) 时 \(y\geq A_i\);当 \(y<A_i\) 时 \(x\geq A_i\)。
继续考虑如何在图上描述这样的限制。我们对每个 \(A_i\) 建立一个点 \(p_i\),连 \(A_i\) 在 \(x\) 中对应的点 \(\to\) \(p_i\) \(\to\) \(A_i+1\) 在 \(y\) 中对应的点,表示 \(x<A_i\) 时 \(y\geq A_i\);反过来也同理。
答案就是最小割。总点数为 \(O(nV)\),其中 \(V\) 是值域。
点击查看代码
#include <bits/stdc++.h>
#include <atcoder/maxflow>
#define FL(i, a, b) for (int i = (a); i <= (b); ++i)
#define FR(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std;
using namespace atcoder;
constexpr int N = 3e5 + 10, M = 3e6 + 10, V = 500;
constexpr int INF = 1e9 + 10;
int n, m, s, t, cnt = 1;
int Id(int i, int j) {
return (i - 1) * (V + 1) + j;
}
int main() {
scanf("%d %d", &n, &m);
s = n * 2 * (V + 1) + m + 1, t = s + 1;
mf_graph<int> G(t + 1);
FL(i, 1, n) {
int x;
scanf("%d", &x);
G.add_edge(s, Id(i, x), INF);
G.add_edge(Id(i, V + 1), t, INF);
FL(j, 1, V) {
G.add_edge(Id(i, j), Id(i, j + 1), j);
G.add_edge(Id(i, j + 1), Id(i, j), INF);
}
}
FL(i, n + 1, n * 2) {
int x;
scanf("%d", &x);
G.add_edge(s, Id(i, 1), INF);
G.add_edge(Id(i, V + 2 - x), t, INF);
FL(j, 1, V) {
G.add_edge(Id(i, j), Id(i, j + 1), V - j + 1);
G.add_edge(Id(i, j + 1), Id(i, j), INF);
}
}
FL(i, 1, m) {
FL(j, 1, n) {
int x;
scanf("%d", &x);
G.add_edge(n * 2 * (V + 1) + i, Id(j, x), INF);
G.add_edge(Id(j + n, V + 2 - x), n * 2 * (V + 1) + i, INF);
}
}
printf("%d\n", G.flow(s, t));
return 0;
}