BZOJ 1597:[Usaco2008 Mar]土地购买(斜率优化)

来源:互联网 发布:和孩子一起学编程 pdf 编辑:程序博客网 时间:2024/05/18 09:06

BZOJ 1597:[Usaco2008 Mar]土地购买

题意概述:

n(1<=n<=5* 10^4)个矩形,多个矩形购买价格为其长宽(1*10^6)的最值乘积,求如何分组使得代价最小.

题目分析:

1.设矩形长宽为x,y;若有xi<=xj&&yi<=yj,那么i号矩形是无用的(可以被j号矩形包括,对答案无贡献),可以以xi< xj||(xi==xj&&yi< yj)排序.

2.排序以后可以发现,剩下的矩形必定是x递增,y递减的;那么可以定义数组ans,那么转移方程为

3.显然这个方法的时间复杂度是O(n^2)的,而n<=5* 10^4,那么假设当前为i号矩形,若要使j号能比k号将i更优化,则有

式子左边就得到了一个类似于斜率的东西,那么只要斜率< x[i],k号矩形就是无用的,不用从这里转移过来.

4.既然已经推导到了式子,那么就可以用一个斜率单调递减队列que来维护(que中存储编号):对于i号位置的ans要转移更新时,比较队首的两个元素斜率,若满足

则队首元素可以弹出,继续往下找;
完成更新之后的i号要进队的时候,比较队尾的两个元素斜率和队尾与当前点的斜率,若满足

则队尾元素可以弹出,继续往上找.

代码:

#include<cstdio>#include<iostream>#include<algorithm>using namespace std;typedef long long ll;const int maxn=500000+10;struct Land {    int x,y;    bool operator < (const Land& rhs) const {        return x<rhs.x||(x==rhs.x&&y<rhs.y);    }    void input() {        scanf("%d%d",&x,&y);    }}land[maxn],st[maxn];ll ans[maxn];int que[maxn];double slope(int a,int b){    return 1.0*(ans[b]-ans[a])/(st[a+1].y-st[b+1].y);}int main(){    int n;    scanf("%d",&n);    for(int i=1;i<=n;i++) land[i].input();    sort(land+1,land+n+1);    int top=0;    for(int i=1;i<=n;i++) {//去除无用矩形        while(top&&land[i].y>=st[top].y) --top;        st[++top]=land[i];    }    int head=0,tail=0;    for(int i=1;i<=top;i++) {//斜率优化dp转移        while(head<tail&&slope(que[head],que[head+1])<st[i].x) ++head;//由于x递增,所以之前删去的矩形之后一定不用其转移        ans[i]=ans[que[head]]+(ll)st[que[head]+1].y*st[i].x;        while(head<tail&&slope(que[tail-1],que[tail])>slope(que[tail],i)) --tail;//维护斜率单调递减        que[++tail]=i;    }    printf("%lld",ans[top]);    return 0;}
0 0