【BZOJ1597】土地购买

题目

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1597
农夫John准备扩大他的农场,他正在考虑 \(N\) 块长方形的土地。每块土地的价格是它的面积,但FJ可以同时购买多快土地。 这些土地的价格是它们最大的长乘以它们最大的宽, 但是土地的长宽不能交换。 如果FJ买一块 \(3\times 5\) 的地和一块 \(5\times 3\) 的地,则他需要付 \(5\times 5=25\)。FJ希望买下所有的土地,但是他发现分组来买这些土地可以节省经费。 他需要你帮助他找到最小的经费。

思路

经典的斜率优化题。基本上会板子就可以做出来吧(
如果两块土地分别为 \(a_i\times b_i\)\(a_j\times b_j\),且满足 \(a_i\geq a_j,b_i\geq b_j\),那么显然把这两块土地划进一个集合里会最优。因为其中一块土地不会产生代价。换句话说,如果一块土地 \(i\) 会被其他土地完全包含,那么 \(i\) 是不会对答案有贡献的。
那么将完全包含的土地剔除后,将剩余土地按长从小到大排序,那么宽一定是严格降序的。那么为了最优,显然应该划分成若干连续的区间。同时如果将 \([i,j]\) 划分成一个集合,那么产生的代价是 \(a_j\times b_i\)
所以设前 \(i\) 片土地全部买下来所需最小代价为 \(f[i]\),则有

\[f[i]=\min(f[j]+a_i\times b_{j+1}) \]

化简成 \(f[j]=-a_i\times b_{j+1}+f[i]\),那么每一个决策点对应的就是 \((-b_{j+1},f_j)\)。由于 \(a_i\) 满足单调性,所以单调队列维护下凸壳即可。
时间复杂度 \(O(n)\)

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N=50010,M=1000010;
int n,m,a[N],b[N],q[N];
ll f[N];

struct node
{
	int a,b;
	bool flag;
}c[N];

bool cmp(node x,node y)
{
	if (x.a<y.a) return 1;
	if (x.a>y.a) return 0;
	return x.b<y.b;
}

int main()
{
	scanf("%d",&m);
	for (int i=1;i<=m;i++)
		scanf("%d%d",&c[i].a,&c[i].b);
	sort(c+1,c+1+m,cmp);
	for (int i=m,maxn=0;i>=1;i--)
		if (c[i].b<=maxn) c[i].flag=1;
			else maxn=c[i].b;
	for (int i=1;i<=m;i++)
		if (!c[i].flag)
		{
			n++;
			a[n]=c[i].a; b[n]=c[i].b;
		}
	int l=1,r=1;
	for (int i=1;i<=n;i++)
	{
		for (int x=q[l],y=q[l+1];l<r;x=q[l],y=q[l+1])
			if (l<r && f[y]-f[x]<=1LL*a[i]*(b[x+1]-b[y+1])) l++;
				else break;
		f[i]=f[q[l]]+1LL*a[i]*b[q[l]+1];
		for (int x=q[r-1],y=q[r];l<r;x=q[r-1],y=q[r])
			if (l<r && (f[y]-f[x])*(b[y+1]-b[i+1])>=(f[i]-f[y])*(b[x+1]-b[y+1])) r--;
				else break;
		q[++r]=i;
	}
	printf("%lld",f[n]);
	return 0;
}
/*
4
100 1
15 15
20 5
1 100
*/
原文地址:https://www.cnblogs.com/stoorz/p/12554157.html