目录
显示
题目链接
https://www.luogu.org/problem/P2900
题解
对于两块土地 \(i,j\),如果 \(w_i<w_j\) 且 \(h_i<h_j\),那么将这两块土地一块购买,\(i\) 土地对于答案就不会产生任何贡献。
因此我们将所有土地按长度为第一关键字,宽度为第二关键字降序排序,同时排除那些对答案没有贡献的土地。
现在容易发现所有土地的长是递减,宽是递增的。这种情况下,显然将连续一段的土地一起购买比将多个不连续的段一块购买更优。
设 \(f(i)\) 表示前 \(i\) 块土地的最低花费。
容易得到如下状态转移方程:
$$f(i)=\min_{j=1}^{i-1} \{f_j+w_i \times h_{j+1}\}$$
时间复杂度是 \(O(n^2)\) 的,需要优化。
考虑上斜率优化。
假如存在两个决策点 \(a,b\),且 \(a<b\)。当以 \(a\) 处的决策为更优决策时:
$$f_a+w_i \times h_{a+1} < f_b + w_i \times h_{b+1}$$
$$\frac{f_a-f_b}{h_{b+1}-h_{a+1}} < w_i$$
用单调队列维护即可。时间复杂度 \(O(n)\)。
#include <iostream>
#include <algorithm>
using namespace std;
struct node
{
long long x,y;
}a[50005];
long long f[50005];
int q[50005];
bool cmp(const node&a,const node&b)
{
return a.x>b.x||(a.x==b.x&&a.y>b.y);
}
double slope(int x,int y)
{
return double(f[x]-f[y])/(a[y+1].x-a[x+1].x);
}
int main()
{
int n,cnt=0;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i].x>>a[i].y;
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
if(a[cnt].y<a[i].y)a[++cnt]=a[i];
int l=0,r=0;
for(int i=1;i<=cnt;i++)
{
while(l<r&&slope(q[l],q[l+1])<=a[i].y)
l++;
f[i]=f[q[l]]+a[q[l]+1].x*a[i].y;
while(l<r&&slope(q[r-1],q[r])>=slope(q[r],i))
r--;
q[++r]=i;
}
cout<<f[cnt]<<endl;
return 0;
}