我们可以把$(\sum A, \sum B)$看做平面上的点,那么就是要找到下凸壳上与$y = -x + b$这个直线系上某条支线刚好相切的切点
现在可以知道下凸壳上最左边的点$X$和最下面的点$Y$
于是我们可以先找到与直线$XY$距离最远的点$Z$,然后查看答案点是在$XZ$、$YZ$中的哪一段,递归下去就好了
关于如何计算$Z$:
经过$X$、$Y$两点的直线$Ax + By + C = 0$,$Z(x_0, y_0)$到它最远,故$\frac{|Ax_0 + By_0 + C|} {\sqrt{A^2 + B^2}}$最大
又由于$Z$在$XY$左下,所以等价于$Ax_0 + By_0$最小,而这个是可以用KM做的,于是就做完了
1 /************************************************************** 2 Problem: 3571 3 User: rausen 4 Language: C++ 5 Result: Accepted 6 Time:2356 ms 7 Memory:908 kb 8 ****************************************************************/ 9 10 #include <cstdio> 11 #include <cstring> 12 #include <algorithm> 13 14 using namespace std; 15 typedef long long ll; 16 const int N = 75; 17 const ll inf = (ll) 1e18; 18 19 struct point { 20 int x, y; 21 point(int _x = 0, int _y = 0) : x(_x), y(_y) {} 22 23 inline point operator -(const point &p) const { 24 return point(x - p.x, y - p.y); 25 } 26 inline ll operator * (const point &p) const { 27 return 1ll * x * p.y - 1ll * y * p.x; 28 } 29 }; 30 31 int n; 32 int a[N][N], b[N][N]; 33 ll w[N][N], slack[N]; 34 ll lx[N], ly[N]; 35 int link[N]; 36 bool vx[N], vy[N]; 37 ll ans; 38 39 inline int read() { 40 int x = 0, sgn = 1; 41 char ch = getchar(); 42 while (ch < '0' || '9' < ch) { 43 if (ch == '-') sgn = -1; 44 ch = getchar(); 45 } 46 while ('0' <= ch && ch <= '9') { 47 x = x * 10 + ch - '0'; 48 ch = getchar(); 49 } 50 return sgn * x; 51 } 52 53 bool dfs(int p) { 54 register int i; 55 register ll tmp; 56 vx[p] = 1; 57 for (i = 1; i <= n; ++i) { 58 if (vy[i]) continue; 59 tmp = lx[p] + ly[i] - w[p][i]; 60 if (!tmp) { 61 vy[i] = 1; 62 if (link[i] == -1 || dfs(link[i])) { 63 link[i] = p; 64 return 1; 65 } 66 } else slack[i] = min(slack[i], tmp); 67 } 68 return 0; 69 } 70 71 inline point KM() { 72 static int i, j; 73 static ll d; 74 static int resa, resb; 75 memset(ly, 0, sizeof(ly)); 76 memset(link, -1, sizeof(link)); 77 for (i = 1; i <= n; ++i) 78 for (lx[i] = -inf, j = 1; j <= n; ++j) 79 lx[i] = max(lx[i], w[i][j]); 80 for (i = 1; i <= n; ++i) { 81 for (j = 1; j <= n; ++j) slack[j] = inf; 82 while (1) { 83 memset(vx, 0, sizeof(vx)); 84 memset(vy, 0, sizeof(vy)); 85 if (dfs(i)) break; 86 for (d = inf, j = 1; j <= n; ++j) 87 if (!vy[j]) d = min(d, slack[j]); 88 for (j = 1; j <= n; ++j) 89 if (vx[j]) lx[j] -= d; 90 for (j = 1; j <= n; ++j) 91 if (vy[j]) ly[j] += d; 92 else slack[j] -= d; 93 } 94 } 95 for (resa = resb = 0, i = 1; i <= n; ++i) 96 if (link[i] != -1) 97 resa += a[link[i]][i], resb += b[link[i]][i]; 98 ans = min(ans, 1ll * resa * resb); 99 return point(resa, resb); 100 } 101 102 inline void work(point L, point R) { 103 register int i, j; 104 register point p = R - L, mid; 105 for (i = 1; i <= n; ++i) 106 for (j = 1; j <= n; ++j) 107 w[i][j] = p * point(a[i][j], b[i][j]); 108 mid = KM(); 109 if ((mid - R) * (L - R) <= 0) return; 110 work(L, mid), work(mid, R); 111 } 112 113 int main() { 114 int T, i, j; 115 point L, R; 116 T = read(); 117 while (T--) { 118 n = read(), ans = inf; 119 for (i = 1; i <= n; ++i) 120 for (j = 1; j <= n; ++j) a[i][j] = read(); 121 for (i = 1; i <= n; ++i) 122 for (j = 1; j <= n; ++j) b[i][j] = read(); 123 for (i = 1; i <= n; ++i) 124 for (j = 1; j <= n; ++j) 125 w[i][j] = -a[i][j]; 126 R = KM(); 127 for (i = 1; i <= n; ++i) 128 for (j = 1; j <= n; ++j) 129 w[i][j] = -b[i][j]; 130 L = KM(); 131 work(L, R); 132 printf("%lld\n", ans); 133 } 134 return 0; 135 }