BZOJ3571 [Hnoi2014]画框

我们可以把$(\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 }
View Code

 

转载于:https://www.cnblogs.com/rausen/p/4458677.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值