[USACO 08MAR]土地购买

Description

题库链接

给你 $n$ 块不同大小的土地。你可分批购买这些土地,每一批价格为这一批中最大的长乘最大的宽。问你买下所有土地的花费最小为多少。

$1\leq n\leq 50000$

Solution

有一个贪心的思想就是一块土地如果是 $w_i\times h_i$,若 $\exists j$ 满足 $w_i\leq w_j,h_i\leq h_j$。那么 $i$ 这块土地一定不会参与到花费计算的。这是因为 $i$ 这块土地可以放在 $j$ 那一批,然后 $i$ 的长和宽一定不是最大的。

因此我们把这些土地按 $w$ 从小到大排序,那么 $h$ 一定是单调递减的。基于此,我们还可以得出另外一个结论,同一批次在这个序列中一定是连续的一段。这是因为这一段的 $w,h$ 最大值一定是在端点取,你把中间某个地不放在这一段是对这一段的花费不产生影响的,因此不如选上这一个点。

基于上述两个结论,我们可以对排完序的序列进行 DP。记 $f_i$ 表示购买 $1\sim i$ 的土地的最小的花费,那么 $f_i=\min{f_j+w_i\times h_j}$。

由于 $w_i$ 和 $h_j$ 单调性相反,因此可以用单调队列维护上凸包斜率优化解决。

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 50000+5;

int n, tot, q[N], head, tail;
struct tt {
    int w, h;
    bool operator < (const tt &b) const {
        return w == b.w ? h < b.h : w < b.w;
    }
} a[N], b[N];
long long f[N];

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d%d", &a[i].w, &a[i].h);
    sort(a+1, a+n+1);
    for (int i = 1; i <= n; i++) {
        while (tot && b[tot].h <= a[i].h) --tot;
        b[++tot] = a[i];
    } 
    n = tot; tail = -1;
    for (int i = 1; i <= n; i++) {
        while (head < tail && (-f[i-1]+f[q[tail]-1])*(b[i].h-b[q[tail-1]].h)
        <= (-f[i-1]+f[q[tail-1]-1])*(b[i].h-b[q[tail]].h)) --tail;
        q[++tail] = i;
        while (head < tail && -f[q[head]-1]+f[q[head+1]-1] <= 1ll*b[i].w*(b[q[head]].h-b[q[head+1]].h)) ++head;
        f[i] = f[q[head]-1]+1ll*b[q[head]].h*b[i].w;
    }
    printf("%lld\n", f[n]);
    return 0;
}
posted @ 2020-02-03 22:38  NaVi_Awson  阅读(202)  评论(0编辑  收藏  举报