BZOJ 1597: [Usaco2008 Mar]土地购买
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 6061 Solved: 2298
[Submit][Status][Discuss]
Description
农夫John准备扩大他的农场,他正在考虑N (1 <= N <= 50,000) 块长方形的土地. 每块土地的长宽满足(1 <= 宽 <
= 1,000,000; 1 <= 长 <= 1,000,000). 每块土地的价格是它的面积,但FJ可以同时购买多快土地. 这些土地的价
格是它们最大的长乘以它们最大的宽, 但是土地的长宽不能交换. 如果FJ买一块3x5的地和一块5x3的地,则他需要
付5x5=25. FJ希望买下所有的土地,但是他发现分组来买这些土地可以节省经费. 他需要你帮助他找到最小的经费.
Input
- 第1行: 一个数: N
第2..N+1行: 第i+1行包含两个数,分别为第i块土地的长和宽
Output第一行: 最小的可行费用.
Sample Input
4
100 1
15 15
20 5
1 100
输入解释:
共有4块土地.
Sample Output
500
FJ分3组买这些土地:
第一组:100x1,
第二组1x100,
第三组20x5 和 15x15 plot.
每组的价格分别为100,100,300, 总共500.
题解
玄学斜率优化,首先我们可以将那些“免费送的地”去了,免费送就是将它按长度降序排序,维护一个宽度
递增的序列,那些去掉的即是免费送的。然后我们考虑转移方程,因为此时的w单调递增,l单调递减。
f[i]=min(f[j]+l[j+1]*w[i])
然后我们再YY出一个k,使得k<j且f[j]更优。
f[j]+l[j+1]*w[i]<f[k]+l[k+1]*w[i],整理得
(f[j]-f[k])/(l[k+1]-l[j+1])>w[i],我们将前面的式子当做斜率,每次判断斜率是否大于新增宽度,
维护一个单调队列,然后用队头更新。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 50005;
inline int rd(){
int x=0,f=1;char ch=getchar();
while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0' && ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
struct Ground{
int l,w;
}g[MAXN],gr[MAXN];
int n,cnt;
int Q[MAXN],head=1,tail=1;
long long f[MAXN];
inline bool cmp(Ground x,Ground y){
if(x.l==y.l) return x.w>y.w;
return x.l>y.l;
}
inline double slope(int x,int y) {
return (double)(f[x]-f[y])/(double)(gr[y+1].l-gr[x+1].l);
}
int main(){
n=rd();
for(register int i=1;i<=n;i++){
g[i].l=rd();
g[i].w=rd();
}
sort(g+1,g+1+n,cmp);
for(register int i=1;i<=n;i++){
if(g[i].w<=gr[cnt].w) continue;
gr[++cnt].l=g[i].l;
gr[cnt].w=g[i].w;
}
for(register int i=1;i<=cnt;i++){
while(head<tail && slope(Q[head],Q[head+1])<=gr[i].w) head++;
f[i]=f[Q[head]]+(long long)gr[i].w*gr[Q[head]+1].l;
while(head<tail && slope(Q[tail-1],Q[tail])>=slope(Q[tail],i)) tail--;
Q[++tail]=i;
}
printf("%lld",f[cnt]);
return 0;
}