P2900 [USACO08MAR]Land Acquisition G

https://www.luogu.com.cn/problem/P2900
(n) 块地,给出每块的长和宽
每次可以购买一个或多个地,此次购买的代价是这些地中最大的宽乘以最大的长
问最小花费多少代价能把所有地都买到

斜率优化dp

首先想到,如果有一个地比另一个地的长和宽都要小,显然可以不考虑这块地了
忽略这种地的过程,可以按 (l) 为第一关键字,(w) 为第二来升序排个序,然后用栈维护一下就好
是这样一个过程:

std::sort(a+1,a+1+n,cmp);
for(reg int i=1;i<=n;i++){
	while(top&&b[top].w<=a[i].w&&b[top].l<=a[i].l) top--;
	b[++top]=(data){a[i].w,a[i].l};
}

这样处理完以后(按照在 b 数组里的顺序来看),(l) 是单调不减的
所以 (w) 应该是单调减,因为如果它不变或者递增的话,就会被当作不用处理的地来忽略掉

然后一次购买的多块地,在这种排序方式下,一定是连续的
假设购买了 (x,x+2) 两块地,那么花费是 (w_x l_{x+2}),然后又因为 (w_{x+1}<w_x,l_{x+1}le l_{x+2})
所以完全可以在不增加任何代价的情况下将第 (x+1) 块地包括进去

那么可以写出dp的转移方程了 (f_i=min{f_j+w_{j+1} l_i})
但这样是 (O(n^2)) 的,还需要个斜率优化
首先可以确定 (b=f_i),比较习惯最小化截距,不过题解区里好像还有最小化 (y) 的?
然后 (w_{j+1} l_i) 肯定是解析式里的 (kx)
(k) 应该和 (i) 有关,所以 (k=l_i,x=-w_{j+1})
那么 (y) 就是 (f_j)

所以:(f_j=l_i(-w_{j+1})+f_i)
因为 (l) 递增,所以符合 (k) 递增的条件,可以不用二分
(w) 递减,所以 (-w) 递增,也符合 (x) 递增的要求

似乎就可以了,放上代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
#define N 50005
struct data{
	LL w,l;
}a[N],b[N];
int top,n;
int tail,head;
int q[N];
LL f[N];
inline int cmp(data a,data b){
	return a.l==b.l?a.w<b.w:a.l<b.l;
}
inline LL get_x(int i){return -b[i+1].w;}
inline LL get_y(int i){return f[i];}
int main(){
	n=read();
	for(reg int i=1;i<=n;i++) a[i].w=read(),a[i].l=read();
	std::sort(a+1,a+1+n,cmp);
	for(reg int i=1;i<=n;i++){
		while(top&&b[top].w<=a[i].w&&b[top].l<=a[i].l) top--;
		b[++top]=(data){a[i].w,a[i].l};
	}
	n=top;
	tail=head=0;q[0]=0;
	for(reg int i=1;i<=n;i++){//k=l[i]
		while(tail<head&&
		get_y(q[tail+1])-get_y(q[tail])<=b[i].l*(get_x(q[tail+1])-get_x(q[tail]))) tail++;
		int j=q[tail];
		f[i]=f[j]+b[j+1].w*b[i].l;
		while(tail<head&&(get_y(q[head])-get_y(q[head-1]))*(get_x(i)-get_x(q[head]))
		>=(get_y(i)-get_y(q[head]))*(get_x(q[head])-get_x(q[head-1]))) head--;
		q[++head]=i;
	}
	std::printf("%lld",f[n]);
	return 0;
}
原文地址:https://www.cnblogs.com/suxxsfe/p/13155273.html