土地购买 / Land Acquisition G / ACQUIRE - Land Acquisition
题目链接:ybt金牌导航1-3-2 / luogu P2900 / luogu SP15086
题目大意
有一些土地,然后你可以把它们划分成任意组。
每组的花费是组里面的土地的长的最大值乘宽的最大值。
问你最小花费是多少。
思路
首先,我们要看能不能贪心一小下,因为都是取最大值嘛。
那你会想到,对于长或者宽,你肯定是要选大的就选很多大的。也就是说,我们可以把东西按长或宽从大到小排个序,然后每次就选一个连续的段。
那你还会想到,按这么搞有些土地是完全对答案没有贡献的。
没错,就是长宽都比人小的,那这些我们可以直接去掉不要。
那剩下的数就是长是降序,宽是升序的。
那如果选了区间
l
∼
r
l\sim r
l∼r,那花费就是
x
l
×
y
r
x_l\times y_r
xl×yr。
然后你考虑一下 DP。
设
f
i
f_i
fi 为选前
i
i
i 个东西的最小花费。
很容易列出方程:
f
i
=
min
0
≤
j
≤
i
{
f
j
+
w
j
+
1
,
i
}
f_i=\min\limits_{0\leq j\leq i}\{f_j+w_{j+1,i}\}
fi=0≤j≤imin{fj+wj+1,i}
(
w
i
,
j
=
x
i
×
y
j
w_{i,j}=x_i\times y_j
wi,j=xi×yj)
那看到这个东西,就很容易想到斜率优化、决策单调性之类的。
然后证明一下,你会发现它满足四边形不等式。
那你就可以开搞。
我这里用的是二分的决策单调性。
然后就好了。
代码
#include<cstdio>
#include<algorithm>
using namespace std;
struct node {
int x, y;
}a[50001], p[50001];
int n, nn, sta[50001], l[50001], top, low, r[50001], maxn;
long long f[50001];
bool cmp(node x, node y) {
if (x.x != y.x) return x.x > y.x;
return x.y > y.y;
}
long long clac(int x, int y) {
return f[x] + 1ll * p[x + 1].x * p[y].y;
}
int main() {
scanf("%d", &nn);
for (int i = 1; i <= nn; i++) scanf("%d %d", &a[i].x, &a[i].y);
sort(a + 1, a + nn + 1, cmp);//贪心排序
n = 1;
p[n] = a[1];
maxn = a[1].y;
for (int i = 2; i <= nn; i++)//把没用的土地去掉
if (a[i].y > maxn) {
p[++n] = a[i];
maxn = a[i].y;
}
top = 1;
low = 1;
sta[1] = 0;
l[1] = 0;
r[1] = n;
for (int i = 1; i <= n; i++) {//决策单调性来做
if (l[low] < r[low]) l[low]++;
else low++;
f[i] = clac(sta[low], i);
if (clac(i, n) >= clac(sta[top], n)) continue;
while (low <= top && clac(i, l[top]) <= clac(sta[top], l[top]))
top--;
if (low > top) {
top++;
sta[top] = i;
l[top] = i;
r[top] = n;
continue;
}
int lef = l[top], rig = r[top], re;
while (lef <= rig) {
int mid = (lef + rig) >> 1;
if (clac(i, mid) <= clac(sta[top], mid)) {
re = mid;
rig = mid - 1;
}
else lef = mid + 1;
}
r[top] = re - 1;
top++;
sta[top] = i;
l[top] = re;
r[top] = n;
}
printf("%lld", f[n]);
return 0;
}