[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

相关文章: