BZOJ2406 矩阵 <上下界网络流>

Problem

矩阵

Time Limit: 10Sec10 Sec
Memory Limit: 128MB128 MB

Description

![][1]

Input

第一行两个数nnmm,表示矩阵的大小。
接下来nn行,每行mm列,描述矩阵AA
最后一行两个数LLRR

Output

一行,输出最小的答案。

Sample Input

1
2
3
4
2 2
0 1
2 1
0 1

Sample Output

1
1

HINT

N,M200N,M\le 200, 0LR10000\le L\le R\le 1000, 0Ai,j10000\le A_{i,j}\le 1000

标签:线性规划上下界网络流

Solution

线性规划转上下界网络流。
看到所求为最大值中的最小,可想到二分答案。
对于当前答案tanstans​,验证是否能构造矩阵B使得:

  • 对于i[1,n], j[1,m]\forall i\in [1,n],\ \forall j\in[1,m]LBi,jRL\le B_{i,j}\le R
  • 对于i[1,n]\forall i \in [1,n]j=1mAi,jtansj=1mBi,jj=1mAi,j+tans\sum_{j=1}^{m}{A_{i,j}}-tans \le \sum_{j=1}^{m}{B_{i,j}} \le \sum_{j=1}^{m}{A_{i,j}}+tans
  • 对于j[1,m]\forall j\in [1,m]i=1nAi,jtansi=1nBi,ji=1nAi,j+tans\sum_{i=1}^{n}{A_{i,j}}-tans \le \sum_{i=1}^{n}{B_{i,j}} \le \sum_{i=1}^{n}{A_{i,j}}+tans
    n,mn,m又只有200,不难想到跑网络流验证。

将每行每列设为点(共n+mn+m个),建图:

  • SrowiS\to row_i 容量[j=1mAi,jtans,  j=1mAi,j+tans][\sum_{j=1}^{m}{A_{i,j}}-tans,\ \ \sum_{j=1}^{m}{A_{i,j}}+tans]
  • columnjTcolumn_j \to T 容量[i=1nAi,jtans,  i=1nAi,j+tans][\sum_{i=1}^{n}{A_{i,j}}-tans, \ \ \sum_{i=1}^{n}{A_{i,j}}+tans]
  • rowicolumnjrow_i\to column_j 容量[L,  R][L,\ \ R]

赫然是个上下界网络流。
建虚拟源虚拟汇跑最大流看是否等于补流即可。
建图,SS,TTSS,TT为上下界网络流的原源和原汇,S,TS,T为虚拟源和汇,d[]d[]记录补流:

  • TTSSTT\to SS 容量\infty
  • rowicolumnjrow_i\to column_j​ 容量RLR-L​d[rowi]+L,  d[columnj]Ld[row_i] +L,\ \ d[column_j] - L​
  • SSrowiSS\to row_i 容量j=1mAi,jtans\sum_{j=1}^{m}{A_{i,j}}-tansd[SS]+2×tans,  d[rowi]2×tansd[SS]+2\times tans,\ \ d[row_i]-2\times tans
  • columnjTTcolumn_j \to TT 容量i=1nAi,jtans\sum_{i=1}^{n}{A_{i,j}}-tansd[columnj]+2×tans,  d[TT]2×tansd[column_j]+2\times tans,\ \ d[TT]-2\times tans

随后将此图和虚拟源汇接上:
对于x{row1n}{column1m}{SS,TT}x\in \{row_{1\sim n}\}\cup \{column_{1\sim m}\}\cup \{SS,TT\}

  • d[x]<0d[x] < 0,连接SxS\to x 容量d[x]-d[x]totd[x]tot_补-d[x]
  • d[x]>0d[x] > 0,连接xTx \to T 容量d[x]d[x]tot+d[x]tot_分+d[x]

最后判断tot=tot=MaxFlowtot_补=tot_分=MaxFlow

Code

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
#include <bits/stdc++.h>
#define MAX_N 500000
#define MAX_M 500000
#define INF 0x3f3f3f3f
#define mid ((p+q)>>1)
using namespace std;
template <class T>
inline void read(T &x) {
x = 0; int c = getchar(), f = 1;
for (; !isdigit(c); c = getchar()) if (c == 45) f = -1;
for (; isdigit(c); c = getchar()) (x *= 10) += f*(c-'0');
}
int r, c, x[205], y[205], P, Q;
int n, s, t, ss, tt, cnt, d[MAX_N+5], pr[MAX_N+5], cr[MAX_N+5];
struct node {int v, c, nxt;} E[MAX_M+5];
void init() {cnt = 0, s = 0, t = r+c+1, ss = t+1, tt = t+2, memset(pr, -1, sizeof pr), memset(d, 0, sizeof d);}
void insert(int u, int v, int c) {E[cnt] = (node){v, c, pr[u]}, pr[u] = cnt++;}
void addedge(int u, int v, int c) {insert(u, v, c), insert(v, u, 0);}
bool BFS() {
queue <int> que; que.push(s);
memset(d, -1, sizeof d), d[s] = 0;
while (!que.empty()) {
int u = que.front(); que.pop();
for (int i = pr[u]; ~i; i = E[i].nxt) {
int v = E[i].v, c = E[i].c;
if (~d[v] || !c) continue;
d[v] = d[u]+1, que.push(v);
}
}
return ~d[t];
}
int DFS(int u, int flow) {
if (u == t) return flow; int ret = 0;
for (int i = pr[u]; ~i; i = E[i].nxt) {
int v = E[i].v, c = E[i].c;
if (d[u]+1 != d[v] || !c) continue;
int tmp = DFS(v, min(flow, c));
E[i].c -= tmp, E[i^1].c += tmp;
flow -= tmp, ret += tmp;
if (!flow) break;
}
if (!ret) d[u] = -1; return ret;
}
int Dinic() {int ret = 0; while (BFS()) ret += DFS(s, INF); return ret;}
bool check(int tans) {
int into = 0, outo = 0; init(), n = r+c+2, addedge(tt, ss, INF);
for (int i = 1; i <= r; i++) for (int j = 1; j <= c; j++)
addedge(i, j+r, Q-P), d[i] += P, d[j+r] -= P;
for (int i = 1; i <= r; i++) addedge(ss, i, 2*tans), d[ss] += x[i]-tans, d[i] -= x[i]-tans;
for (int i = 1; i <= c; i++) addedge(i+r, tt, 2*tans), d[i+r] += y[i]-tans, d[tt] -= y[i]-tans;
for (int i = 1; i <= tt; i++) if (i^t) {
if (d[i] < 0) addedge(s, i, -d[i]), into -= d[i];
if (d[i] > 0) addedge(i, t, d[i]), outo += d[i];
}
return into == outo && Dinic() == into;
}
int bi_search(int p, int q) {int ret = -1; while (p <= q) if (check(mid)) ret = mid, q = mid-1; else p = mid+1; return ret;}
int main() {
read(r), read(c);
for (int i = 1; i <= r; i++) for (int j = 1, val; j <= c; j++)
read(val), x[i] += val, y[j] += val;
return read(P), read(Q), printf("%d", bi_search(0, 200000)), 0;
}