[CF1083E] The Fair Nut and Rectangles - 斜率优化dp
Description
给定 \(n\) 个平面直角坐标系中左下角为坐标原点,右上角为 \((x_i, y_i)\) 的矩形,每一个矩形拥有权值 \(a_i\),且保证任意两个矩形的面积不会出现包含关系,保证 \(0 \leq a_i \leq x_i \cdot y_i\)。你的任务是选出若干个矩形,使得选出的矩形的面积并减去矩形的权值之和尽可能大。输出最大值。
Solution
矩形按 \(x_i\) 排序,设 \(f[i]\) 表示在前 \(i\) 个矩形中选取若干个并且第 \(i\) 个一定选,此时的最大答案是多少,然后就是简单的斜率优化 dp
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
const int N = 2000005;
int n, a[N], x[N], y[N], f[N], q[N], head = 0, tail = 0;
struct Rect
{
int x, y, a;
bool operator<(const Rect &rhs) const
{
return x < rhs.x;
}
} rect[N];
double Y(int k)
{
return f[k];
}
double X(int k)
{
return x[k];
}
double K(int k)
{
return y[k];
}
double B(double Y, double K, double X)
{
return Y - K * X;
}
double slope(int p, int q)
{
double dy = Y(p) - Y(q);
double dx = X(p) - X(q);
if (abs(dx) < 1e-9)
{
return -1e32;
}
else
return dy / dx;
}
signed main()
{
ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; i++)
cin >> rect[i].x >> rect[i].y >> rect[i].a;
sort(rect + 1, rect + n + 1);
for (int i = 1; i <= n; i++)
x[i] = rect[i].x, y[i] = rect[i].y, a[i] = rect[i].a;
head = 0;
tail = 0;
q[0] = 0;
for (int i = 1; i <= n; i++)
{
while (head < tail && slope(q[head], q[head + 1]) >= K(i))
++head;
if (head <= tail)
{
int j = q[head];
f[i] = f[j] + (x[i] - x[j]) * y[i] - a[i];
}
else
f[i] = x[i] * y[i] - a[i];
while (head < tail && slope(q[tail - 1], q[tail]) < slope(q[tail], i))
--tail;
q[++tail] = i;
}
cout << *max_element(f + 1, f + n + 1) << endl;
}
// Node[0] del and accepted