题解 - [Luogu P3973] [TJOI2015] 线性代数

题目链接

原始题面

题目描述

为了提高智商,ZJY 开始学习线性代数

她的小伙伴菠萝给她出了这样一个问题:给定一个 \(n \times n\) 的矩阵 \(B\) 和一个 \(1 \times n\) 的矩阵 \(C\). 求出一个 \(1×n\) 的 01 矩阵 \(A\), 使得 \(D=(A×B-C)×A^{\sf T}\) 最大,其中 \(A^{\sf T}\)\(A\) 的转置,输出 \(D\)

输入格式

第一行输入一个整数 \(n\). 接下来 \(n\) 行输入 \(B\) 矩阵,第 \(i\) 行第 \(j\) 个数代表 \(B\) 接下来一行输入 \(n\) 个整数,代表矩阵 \(C\). 矩阵 \(B\) 和矩阵 \(C\) 中每个数字都是不过 \(1000\) 的非负整数

输出格式

输出一个整数,表示最大的 \(D\)

样例 #1

样例输入 #1

1
2
3
4
5
3
1 2 1
3 1 0
1 2 3
2 3 7

样例输出 #1

1
2

提示

  • 对于 \(30\%\) 的数据,\(n \leq 15\);
  • 对于 \(100\%\) 的数据,\(1 \leq n \leq 500\);
  • 另外还有两组不计分的 hack 数据,放在 subtask 2 中,数据范围与上面一致

解题思路

式子不难写出来,即求:

\[ \max_{a_i\in\{0,1\}}\left\{\sum_i\sum_j a_ia_jb_{ij}-\sum_i a_ic_i\right\} \]

注意到如果 \(a_i\)\(1\) 则答案会减 \(c_i\), 而如果答案包含 \(b_{ij}\) 的话则需选 \(a_i\)\(a_j\)

所以我们这样建一个二分图:

  • 点集 \(U\)\(n^2\) 个点,内部元素记作 \(u_{ij}\), \(i,j\in 1..n\)
  • 点集 \(V\)\(n\) 个点,内部元素记作 \(v_i\), \(i\in 1..n\)
  • \(u_{ij}\)\(v_i\), \(v_j\) 连边,边权设为 \(\infty\)

另外取源点 \(s\) 和汇点 \(t\), 将 \(s\) 和点集 \(U\) 中所有点连起来,\(t\) 和点集 \(V\) 所有点连起来,边 \((s,u_{ij})\) 的权为 \(b_{ij}\), 边 \((t,v_i)\) 的权为 \(c_i\)

接下来求这个图的最小割

  • 显然 \((u_{ij},v_k)\) 不在最小割内
  • \((s,u_{ij})\) 在最小割内,则说明最终答案不包含 \(b_{ij}\)
  • \((t,v_i)\) 在最小割内,则说明最终答案要减去 \(c_i\)

最终答案即为 \(\sum_i\sum_j b_{ij}\) 减去最小割的边权和

由于最终建的图是二分图,所以 Dinic 复杂度是 \(O(n^3)\)

代码参考

Show code

Luogu_P3973view raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
/*
* @Author: Tifa
* @Description: From <https://github.com/Tiphereth-A/CP-archives>
* !!! ATTENEION: All the context below is licensed under a
* GNU Affero General Public License, Version 3.
* See <https://www.gnu.org/licenses/agpl-3.0.txt>.
*/
#include <bits/stdc++.h>
template <class Tp>
using vc = std::vector<Tp>;
template <class Tp>
using vvc = std::vector<std::vector<Tp>>;
struct CustomHash {
static constexpr uint64_t splitmix64(uint64_t x) {
x += 0x9e3779b97f4a7c15;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
return x ^ (x >> 31);
}
static constexpr size_t append(size_t x, size_t y) {
return x ^ (y >> 1) ^ ((y & 1) << (sizeof(size_t) * 8 - 1));
}
size_t operator()(uint64_t x) const {
static const uint64_t FIXED_RANDOM =
std::chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x + FIXED_RANDOM);
}
template <class Tp, class Up>
size_t operator()(std::pair<Tp, Up> const &p) const {
return append((*this)(p.first), (*this)(p.second));
}
template <typename... Ts>
size_t operator()(std::tuple<Ts...> const &tp) const {
size_t ret = 0;
std::apply(
[&](Ts const &...targs) { ((ret = append(ret, (*this)(targs))), ...); },
tp);
return ret;
}
template <
class Tp,
std::enable_if_t<std::is_same_v<decltype(std::declval<Tp>().begin()),
typename Tp::iterator> &&
std::is_same_v<decltype(std::declval<Tp>().end()),
typename Tp::iterator>> * = nullptr>
size_t operator()(Tp const &tp) const {
size_t ret = 0;
for (auto &&i : tp) ret = append(ret, (*this)(i));
return ret;
}
};
using i32 = int32_t;
using i64 = int64_t;
#define for_(i, l, r, vars...) \
for (std::make_signed_t<decltype(l + r)> i = (l), i##end = (r), ##vars; \
i <= i##end; \
++i)
#define all_(a) (a).begin(), (a).end()
#define read_var_(type, name) \
type name; \
std::cin >> name
template <class Tp>
constexpr auto chkmin(Tp &a, Tp b) -> bool {
return b < a ? a = b, true : false;
}
template <class Tp>
constexpr auto chkmax(Tp &a, Tp b) -> bool {
return a < b ? a = b, true : false;
}
template <class Tp>
constexpr auto ispow2(Tp i) -> bool {
return i && (i & -i) == i;
}
#define TPL_SIZE_(Tuple) std::tuple_size_v<std::remove_reference_t<Tuple>>
namespace tuple_detail_ {
template <std::size_t Begin, class Tuple, std::size_t... Is>
constexpr auto subtuple_impl_(Tuple &&t, std::index_sequence<Is...>) {
return std::make_tuple(std::get<Is + Begin>(t)...);
}
template <class Tuple, class BinOp, std::size_t... Is>
constexpr auto
apply2_impl_(BinOp &&f, Tuple &&lhs, Tuple &&rhs, std::index_sequence<Is...>) {
return std::make_tuple(
std::forward<BinOp>(f)(std::get<Is>(lhs), std::get<Is>(rhs))...);
}
} // namespace tuple_detail_
template <std::size_t Begin, std::size_t Len, class Tuple>
constexpr auto subtuple(Tuple &&t) {
static_assert(Begin <= TPL_SIZE_(Tuple) && Len <= TPL_SIZE_(Tuple) &&
Begin + Len <= TPL_SIZE_(Tuple),
"Out of range");
return tuple_detail_::subtuple_impl_<Begin>(t,
std::make_index_sequence<Len>());
}
template <std::size_t Pos, class Tp, class Tuple>
constexpr auto tuple_push(Tp &&v, Tuple &&t) {
static_assert(TPL_SIZE_(Tuple) > 0, "Pop from empty tuple");
return std::tuple_cat(subtuple<0, Pos>(t),
std::make_tuple(v),
subtuple<Pos, TPL_SIZE_(Tuple) - Pos>(t));
}
template <class Tp, class Tuple>
constexpr auto tuple_push_front(Tp &&v, Tuple &&t) {
return tuple_push<0>(v, t);
}
template <class Tp, class Tuple>
constexpr auto tuple_push_back(Tp &&v, Tuple &&t) {
return tuple_push<TPL_SIZE_(Tuple)>(v, t);
}
template <std::size_t Pos, class Tuple>
constexpr auto tuple_pop(Tuple &&t) {
static_assert(TPL_SIZE_(Tuple) > 0, "Pop from empty tuple");
return std::tuple_cat(subtuple<0, Pos>(t),
subtuple<Pos + 1, TPL_SIZE_(Tuple) - Pos - 1>(t));
}
template <class Tuple>
constexpr auto tuple_pop_front(Tuple &&t) {
return tuple_pop<0>(t);
}
template <class Tuple>
constexpr auto tuple_pop_back(Tuple &&t) {
return tuple_pop<TPL_SIZE_(Tuple) - 1>(t);
}
template <class Tuple, class BinOp>
constexpr auto apply2(BinOp &&f, Tuple &&lhs, Tuple &&rhs) {
return tuple_detail_::apply2_impl_(
f, lhs, rhs, std::make_index_sequence<TPL_SIZE_(Tuple)>());
}
#define OO_PTEQ_(op) \
template <class Tp, class Up> \
constexpr auto operator op(std::pair<Tp, Up> lhs, \
const std::pair<Tp, Up> &rhs) { \
return {lhs.first op rhs.first, lhs.second op rhs.second}; \
} \
template <class... Ts> \
constexpr auto operator op(std::tuple<Ts...> const &lhs, \
std::tuple<Ts...> const &rhs) { \
return apply2([](auto &&l, auto &&r) { return l op r; }, lhs, rhs); \
} \
template <class Tp, class Up> \
constexpr std::pair<Tp, Up> &operator op##=(std::pair<Tp, Up> &lhs, \
const std::pair<Tp, Up> &rhs) { \
lhs.first op## = rhs.first; \
lhs.second op## = rhs.second; \
return lhs; \
} \
template <class... Ts> \
constexpr auto operator op##=(std::tuple<Ts...> &lhs, \
const std::tuple<Ts...> &rhs) { \
return lhs = lhs op rhs; \
}
OO_PTEQ_(+)
OO_PTEQ_(-)
OO_PTEQ_(*)
OO_PTEQ_(/)
OO_PTEQ_(%)
OO_PTEQ_(&)
OO_PTEQ_(|)
OO_PTEQ_(^)
OO_PTEQ_(<<)
OO_PTEQ_(>>)
#undef OO_PTEQ_
#undef TPL_SIZE_
template <class Tp, class Up>
std::istream &operator>>(std::istream &is, std::pair<Tp, Up> &p) {
return is >> p.first >> p.second;
}
template <class Tp, class Up>
std::ostream &operator<<(std::ostream &os, const std::pair<Tp, Up> &p) {
return os << p.first << ' ' << p.second;
}
template <typename... Ts>
std::istream &operator>>(std::istream &is, std::tuple<Ts...> &p) {
std::apply([&](Ts &...targs) { ((is >> targs), ...); }, p);
return is;
}
template <typename... Ts>
std::ostream &operator<<(std::ostream &os, const std::tuple<Ts...> &p) {
std::apply(
[&](Ts const &...targs) {
std::size_t n{0};
((os << targs << (++n != sizeof...(Ts) ? " " : "")), ...);
},
p);
return os;
}
template <class Ch,
class Tr,
class Ct,
std::enable_if_t<std::is_same_v<decltype(std::declval<Ct>().begin()),
typename Ct::iterator> &&
std::is_same_v<decltype(std::declval<Ct>().end()),
typename Ct::iterator>> * = nullptr>
std::basic_ostream<Ch, Tr> &operator<<(std::basic_ostream<Ch, Tr> &os,
const Ct &x) {
if (x.begin() == x.end()) return os;
for (auto it = x.begin(); it != x.end() - 1; ++it) os << *it << ' ';
os << x.back();
return os;
}
const i32 INF = 0x3f3f3f3f;
using namespace std;
namespace internal {
template <class T>
struct simple_queue {
std::vector<T> payload;
int pos = 0;
void reserve(int n) { payload.reserve(n); }
int size() const { return int(payload.size()) - pos; }
bool empty() const { return pos == int(payload.size()); }
void push(const T &t) { payload.push_back(t); }
T &front() { return payload[pos]; }
void clear() {
payload.clear();
pos = 0;
}
void pop() { pos++; }
};
} // namespace internal
template <class Cap>
struct mf_graph {
public:
mf_graph(): _n(0) {}
explicit mf_graph(int n): _n(n), g(n) {}
int add_edge(int from, int to, Cap cap) {
assert(0 <= from && from < _n);
assert(0 <= to && to < _n);
assert(0 <= cap);
int m = int(pos.size());
pos.push_back({from, int(g[from].size())});
int from_id = int(g[from].size());
int to_id = int(g[to].size());
if (from == to) to_id++;
g[from].push_back(_edge{to, to_id, cap});
g[to].push_back(_edge{from, from_id, 0});
return m;
}
struct edge {
int from, to;
Cap cap, flow;
};
edge get_edge(int i) {
int m = int(pos.size());
assert(0 <= i && i < m);
auto _e = g[pos[i].first][pos[i].second];
auto _re = g[_e.to][_e.rev];
return edge{pos[i].first, _e.to, _e.cap + _re.cap, _re.cap};
}
std::vector<edge> edges() {
int m = int(pos.size());
std::vector<edge> result;
for (int i = 0; i < m; i++) { result.push_back(get_edge(i)); }
return result;
}
void change_edge(int i, Cap new_cap, Cap new_flow) {
int m = int(pos.size());
assert(0 <= i && i < m);
assert(0 <= new_flow && new_flow <= new_cap);
auto &_e = g[pos[i].first][pos[i].second];
auto &_re = g[_e.to][_e.rev];
_e.cap = new_cap - new_flow;
_re.cap = new_flow;
}
Cap flow(int s, int t) { return flow(s, t, std::numeric_limits<Cap>::max()); }
Cap flow(int s, int t, Cap flow_limit) {
assert(0 <= s && s < _n);
assert(0 <= t && t < _n);
assert(s != t);
std::vector<int> level(_n), iter(_n);
internal::simple_queue<int> que;
auto bfs = [&]() {
std::fill(level.begin(), level.end(), -1);
level[s] = 0;
que.clear();
que.push(s);
while (!que.empty()) {
int v = que.front();
que.pop();
for (auto e : g[v]) {
if (e.cap == 0 || level[e.to] >= 0) continue;
level[e.to] = level[v] + 1;
if (e.to == t) return;
que.push(e.to);
}
}
};
auto dfs = [&](auto self, int v, Cap up) {
if (v == s) return up;
Cap res = 0;
int level_v = level[v];
for (int &i = iter[v]; i < int(g[v].size()); i++) {
_edge &e = g[v][i];
if (level_v <= level[e.to] || g[e.to][e.rev].cap == 0) continue;
Cap d = self(self, e.to, std::min(up - res, g[e.to][e.rev].cap));
if (d <= 0) continue;
g[v][i].cap += d;
g[e.to][e.rev].cap -= d;
res += d;
if (res == up) return res;
}
level[v] = _n;
return res;
};
Cap flow = 0;
while (flow < flow_limit) {
bfs();
if (level[t] == -1) break;
std::fill(iter.begin(), iter.end(), 0);
Cap f = dfs(dfs, t, flow_limit - flow);
if (!f) break;
flow += f;
}
return flow;
}
std::vector<bool> min_cut(int s) {
std::vector<bool> visited(_n);
internal::simple_queue<int> que;
que.push(s);
while (!que.empty()) {
int p = que.front();
que.pop();
visited[p] = true;
for (auto e : g[p]) {
if (e.cap && !visited[e.to]) {
visited[e.to] = true;
que.push(e.to);
}
}
}
return visited;
}

private:
int _n;
struct _edge {
int to, rev;
Cap cap;
};
std::vector<std::pair<int, int>> pos;
std::vector<std::vector<_edge>> g;
};
auto solve([[maybe_unused]] int t_ = 0) -> void {
read_var_(int, n);
vvc<i64> b(n, vc<i64>(n));
vc<i64> c(n);
for (auto &i : b)
for (auto &j : i) cin >> j;
for (auto &i : c) cin >> i;
auto encode_b = [&](int i, int j) { return i * n + j; };
auto encode_c = [&](int i) { return n * n + i; };
int s = n * n + n, t = s + 1;
mf_graph<i64> mf(n * n + n + 2);
for_(i, 0, n - 1)
for_(j, 0, n - 1) mf.add_edge(s, encode_b(i, j), b[i][j]);
for_(i, 0, n - 1) mf.add_edge(encode_c(i), t, c[i]);
for_(i, 0, n - 1)
for_(j, 0, n - 1) {
mf.add_edge(encode_b(i, j), encode_c(i), INF);
mf.add_edge(encode_b(i, j), encode_c(j), INF);
}
i64 ans = 0;
for_(i, 0, n - 1) ans += accumulate(all_(b[i]), 0ll);
cout << ans - mf.flow(s, t) << '\n';
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int i_ = 0;
solve(i_);
return 0;
}