BZOJ1597 [Usaco2008 Mar]土地购买 [斜率优化]

土地购买

题目描述见链接 .


\color{red}{正解部分}

将 矩形的 作为 xx, 矩形的 作为 yy, 放到 平面直角坐标系 中,
题目转化 为使用 最小的矩阵面积和 覆盖所有点,

可以发现当矩形 a,ba, b 满足 a.xb.xa.x \le b.xa.yb.ya.y \le b.y 时, 矩形 aa 是可以忽略的,
于是将所有 aa 类矩形代表的坐标从坐标系中抹除, 可以发现剩下的矩形坐标组成了一个 单调递减 的折线,

然后再使用 最小的矩阵面积和 覆盖所有点,
这个时候可以发现 覆盖时 按 横坐标 从左到右分成连续的几段进行覆盖是最优的,
由此可以想到设 F[i]F[i] 表示覆盖前 ii 个点所使用的最小矩形面积和,
状态转移, F[i]=min(F[k]+cost[k+1,i])F[i] = \min(F[k] + cost[k+1, i]), 根据单调性, cost[k+1,i]=hk+1×wicost[k+1, i] = h_{k+1} \times w_i .

直接转移 时间复杂度 O(N2)O(N^2), 考虑优化, 将方程化简为 F[k]=wi×hk+1+F[i]F[k] = -w_i \times h_{k+1} + F[i],
可以发现是 斜率优化 的形式, y=F[k],k=wi,x=hk+1,b=F[i]y = F[k], k = -w_i, x = h_{k+1}, b = F[i] .

目标是使得 F[i]F[i] 最小, 且 斜率负数单调递减, 所以使用 单调队列 维护 下凸包 即可 .


\color{red}{实现部分}

  • 十年 OIOI 一场空, 不开 long longlong\ long 见祖宗 .
#include<bits/stdc++.h>
#define reg register
typedef long long ll;

const int maxn = 50005;

int N;
int Tmp_1;
int que[maxn];

ll F[maxn];

bool vis[maxn];

struct Node{ int h, w; } A[maxn];

bool cmp(Node a, Node b){ return a.w==b.w?a.h<b.h:a.w<b.w; }

double slope(int a, int b){ return (1.0*F[a] - F[b])/(1.0*A[a+1].h - A[b+1].h); }

int main(){
        scanf("%d", &N);
        for(reg int i = 1; i <= N; i ++) scanf("%d%d", &A[i].w, &A[i].h);
        std::sort(A+1, A+N+1, cmp); int max_h = 0;
        for(reg int i = N; i >= 1; i --){ if(A[i].h <= max_h) vis[i] = 1; max_h = std::max(max_h, A[i].h); }
        for(reg int i = 1; i <= N; i ++) if(!vis[i]) A[++ Tmp_1] = A[i];
        N = Tmp_1; int tl = 0, hd = 1; que[++ tl] = 0;
        for(reg int i = 1; i <= N; i ++){
                double cur_k = -1.0*A[i].w;
                while(tl-hd+1 >= 2 && slope(que[hd], que[hd+1]) > cur_k) hd ++;
                F[i] = F[que[hd]] + 1ll*A[que[hd]+1].h*A[i].w;
                while(tl-hd+1 >= 2 && slope(que[tl-1], i) > slope(que[tl], que[tl-1])) tl --;
                que[++ tl] = i;
        }
        printf("%lld\n", F[N]);
        return 0;
}
posted @ 2019-10-05 18:25  XXX_Zbr  阅读(111)  评论(0编辑  收藏  举报