【洛谷2900】[USACO08MAR] Land Acquisition G(斜率优化DP)
- 有\(n\)块\(a_i\times b_i\)的土地,并购一组土地的代价是\(\max\{a_i\}\times\max\{b_i\}\)。
- 求买到所有土地的最小代价。
- \(n\le5\times10^4\)
预处理
直接\(DP\)是不太好搞的。
考虑我们先给土地按\(a_i\)排个序。
若一块土地\(a_i,b_i\)都比另一块土地小,那么我们完全可以让这块土地跟着那块土地一起买而不产生任何影响。
因此,在\(a_i\)递增的情况下,\(b_i\)是递减的。
此时并购肯定是并购一段区间(设其为\([l,r]\)),代价就是\(a_l\times b_r\)。
斜率优化\(DP\)
设\(f_i\)为购买了前\(i\)块土地的代价,转移方程为:
\[f_i=\min_{j=1}^{i-1}(f_j+a_i\times b_{j+1})
\]
假设一个转移点\(y\)优于\(x\)(\(x<y\)),说明:
\[f_x+a_{i}\times b_{x+1}\ge f_y+a_i\times b_{y+1}\\a_i\times(b_{x+1}-b_{y+1})\ge f_{y}-f_x
\]
由于\(x<y\),故\(b_{x+1}>b_{y+1}\),可以直接两边同除:
\[a_i\ge\frac{f_y-f_x}{b_{x+1}-b_{y+1}}
\]
\(a_i\)是递增的,因此我们直接维护一个单调递增的单调队列,每次从队首转移即可。
代码:\(O(nlogn)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50000
#define LL long long
using namespace std;
int n,q[N+5];LL f[N+5];
struct Data
{
int x,y;I Data(CI a=0,CI b=0):x(a),y(b){}
I bool operator < (Con Data& o) Con {return x^o.x?x<o.x:y<o.y;}//按x排序
}s[N+5];
int main()
{
RI i;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",&s[i].x,&s[i].y);
RI t=0;for(sort(s+1,s+n+1),i=1;i<=n;++i) {W(t&&s[i].y>=s[t].y) --t;s[++t]=s[i];}//如果一块土地两维都小于另一块,直接忽略
RI H=1,T=1;for(q[1]=0,i=1;i<=t;q[++T]=i++)//斜率优化DP
{
W(H<T&&f[q[H+1]]-f[q[H]]<=1LL*s[i].x*(s[q[H]+1].y-s[q[H+1]+1].y)) ++H;//所有不再优的转移点直接弹掉
f[i]=f[q[H]]+1LL*s[i].x*s[q[H]+1].y;//转移
W(H<T&&(f[i]-f[q[T]])*(s[q[T-1]+1].y-s[q[T]+1].y)<(f[q[T]]-f[q[T-1]])*(s[q[T]+1].y-s[i+1].y)) --T;//维护单调递增
}return printf("%lld\n",f[t]),0;//输出答案
}
待到再迷茫时回头望,所有脚印会发出光芒