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;
}