P2900 [USACO08MAR] Land Acquisition G
/* Farmer John 准备扩大他的农场,眼前他正在考虑购买 NN 块长方形的土地。 如果 FJ 单买一块土地,价格就是土地的面积。但他可以选择并购一组土地,并购的价格为这些土地中最大的长乘以最大的宽。比如 FJ 并购一块 3×53×5 和一块 5×35×3 的土地,他只需要支付 5×5=255×5=25 元, 比单买合算。 FJ 希望买下所有的土地。他发现,将这些土地分成不同的小组来并购可以节省经费。 给定每份土地的尺寸,请你帮助他计算购买所有土地所需的最小费用 n<=5e4 1.若h w 均小于某一个地 就可以去掉 具体做法可以是,按高为第一关键字,宽为第二关键字从大到小排序,然后上双指针扫一遍。 2.剩下的就是一个高度递减、宽度递增的矩形序列 连续选择最有 f[i]:前i个最小值 f[i]=min(f[j-1]+h[j]*w[i]) (j<=i) O(N^2) f[j-1]=-w[i]*h[j]+f[i] k->w[i] h[j]->x f[j-1]->y f[j-1]=w[i]*(-h[j])+f[i] 用单调时最好时单调增 将(f[j-1],-h[j]) 看作点 显然维护 斜率单调增加即可 斜率>=w[i]的最小值 head:若斜率<w[i] 直接++ tail:不满足斜率严格单增 -- 斜率k[pos_que]单调队列在pos的斜率关系 f[i]标号 前i个 f[-1]在计算中当作0处理 */ /* 4 100 1 15 15 20 5 1 100 500 */ #include<cstdio> #include<iostream> #include<algorithm> #include<cmath> #include<string.h> #include<queue> #include<vector> #include<bits/stdc++.h> typedef long long ll; #define ddd printf("-----------------------\n"); using namespace std; const int maxn=1e1 +10; const int mod=998244353; const int inf=0x3f3f3f3f; int n,q[50010]; double f[50010],k[50010]; struct node{ int w,h; bool operator<(const node &a)const{ return h>a.h||(h==a.h)&&(w>a.w); } }a[50010]; double cal(int i,int j){ return 1.0*(f[j-1]-f[i-1])/(a[i].h-a[j].h); } int main() { ios::sync_with_stdio(false); cin>>n; for(int i=1;i<=n;i++) cin>>a[i].w>>a[i].h; sort(a+1,a+1+n); int tmp=1;for(int i=1;i<=n;i++) if(a[tmp].w<a[i].w) a[++tmp]=a[i]; n=tmp; int head=1,tail=0; for(int i=1;i<=n;i++) { //j<=i 所以一定要先tail++ while(head<tail&&k[tail-1]>=cal(q[tail],i)) tail--; k[tail]=cal(q[tail],i),q[++tail]=i;//k[pos]:pos向右的一段斜率 while(head<tail&&k[head]<a[i].w) head++; f[i]=f[q[head]-1]+(double)a[q[head]].h*a[i].w; } printf("%.0lf\n",f[n]); return 0; }