洛谷链接:洛谷链接
先将土地按 长度 l
排序,相同长度的土地按 宽度 h
排序,若
h
i
≤
h
j
,
l
i
≤
l
j
h_i \leq h_j,l_i \leq l_j
hi≤hj,li≤lj,可以直接将第 i 块土地和第 j 块土地绑在一起,决策时只需要考虑第 j 块土地。
对排好序的土地维护一个单调栈,栈内土地的 宽度h
单调递减,对最后栈内保留的土地进行 dp
O
(
n
2
)
O(n^2)
O(n2) 的做法:dp[i] = min(dp[j] + l[i] * h[j + 1])
设 j > k
,在 j 转移比在 k 转移更优,满足 dp[j] + l[i] * h[j + 1] < dp[k] + l[i] * h[k + 1]
移项得到: d p [ j ] − d p [ k ] h [ j + 1 ] − h [ k + 1 ] > − l [ i ] \displaystyle\frac{dp[j] - dp[k]}{h[j + 1] - h[k + 1]}>-l[i] h[j+1]−h[k+1]dp[j]−dp[k]>−l[i]
由于 l[i + 1] >= l[i]
,对于 dp[i]
在 j点转移
同样比在 k点转移更优
,决策具有单调性。
设 k < j , g ( j , k ) = d p [ j ] − d p [ k ] h [ j + 1 ] − h [ k + 1 ] k < j,g(j,k) =\displaystyle\frac{dp[j] - dp[k]}{h[j + 1] - h[k + 1]} k<j,g(j,k)=h[j+1]−h[k+1]dp[j]−dp[k]
设 k < j < i k < j < i k<j<i,若 g ( i , j ) ≥ g ( j , k ) g(i,j) \geq g(j,k) g(i,j)≥g(j,k),j 点不可能最优决策点,证明:若 g ( j , k ) > − l [ i ] g(j,k) > -l[i] g(j,k)>−l[i],则 g ( i , j ) > − l [ i ] g(i,j) > -l[i] g(i,j)>−l[i] 一定同时成立, i i i 点比 j j j 点更优;否则 k k k 点比 j j j 点更优
由于这题横坐标单调递减,实际上维护的还是下凸包, i > j i > j i>j,但 x ( i ) < x ( j ) x(i) < x(j) x(i)<x(j),斜率又要单调递增,就是下凸包。
由于决策有单调性(也就是斜率 -l[i]
单调),可以用单调队列维护,在单调队列队头,将
g
(
q
[
f
r
o
n
t
]
,
q
[
f
r
o
n
t
+
1
]
)
>
−
l
[
i
]
g(q[front],q[front + 1]) > -l[i]
g(q[front],q[front+1])>−l[i] 的点全部 pop,-l[i]越来越小
,后面的转移不可能再用到这些点。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4;
typedef long long ll;
int n;
struct node {
int x,y;
bool operator < (const node &rhs) const {
if (x != rhs.x) return x < rhs.x;
return y < rhs.y;
}
}a[maxn];
ll h[maxn],l[maxn],top;
ll dp[maxn];
int q[maxn],rear,front;
ll getup(int x,int y) {
return dp[x] - dp[y];
}
ll getdown(int x,int y) {
return h[x + 1] - h[y + 1];
}
ll calc(int x,int y) {
return dp[y] + 1ll * l[x] * h[y + 1];
}
int main() {
scanf("%d",&n);
for (int i = 1; i <= n; i++) {
scanf("%d%d",&a[i].x,&a[i].y);
}
sort(a + 1,a + n + 1);
for (int i = 1; i <= n; i++) {
while (top > 0 && h[top] <= a[i].y) top--;
h[++top] = a[i].y, l[top] = a[i].x;
}
q[++rear] = 0;
for (int i = 1; i <= top; i++) {
while (front + 1 < rear && getup(q[front + 1],q[front + 2]) >= -l[i] * getdown(q[front + 1],q[front + 2])) front++;
dp[i] = calc(i,q[front + 1]);
while (front + 1 < rear && getup(i,q[rear]) * getdown(q[rear],q[rear - 1]) >= getup(q[rear],q[rear - 1]) * getdown(i,q[rear]))
rear--;
q[++rear] = i;
}
printf("%lld\n",dp[top]);
return 0;
}