P5896 Aliens Sol

终于写了这题!

首先,把题意翻译一下,发现每一个点 \((r,c)\) 相当于要覆盖主对角线上 \([\min\{r,c\},\max\{r,c\}]\) 之间的部分。

那么这个题就转化成了一个区间覆盖问题。

看到区间覆盖的最大次数,想到 wqs 二分。

到这里,可以发现如下性质:

  • 我们可以按照需要覆盖的区间的左端点进行排序。
  • 如果一段需覆盖的区间被另一段需覆盖的区间包含,那么被包含的区间可以被删掉。

下面用 \([l_i,r_i]\) 表示预处理完后每一个 \((r,c)\) 代表的需要覆盖的区间。

  • 结合第一点与第二点,我们发现排序后,需要选择的区间显然满足 \(l_{i-1}<l_i,r_{i-1}<r_i\)
  • wqs 二分代表的实际上是一个斜率始终 \(\le 0\) 的下凸壳。

对于为什么这个东西是一个下凸壳,我们可以感性理解一下。

\(p_k\) 表示强制选择 \(k\) 个区间时的答案。

如果你只能选择 \(k=1\) 个区间,那么实际答案最大可以到覆盖全图。

但是如果选择 \(k=2\) 个区间,你可以在 \(k=1\) 的基础上,把一个右上角的正方形和左下角的正方形(可以为空)去掉,只选择剩下的图形。

且因为 \(p_k\) 表示强制选择 \(k\) 个区间时的答案,所以此时一定贪心地选择能去掉大小最多的方案。

当然答案也可能不变,此时对于 \(k>2\) 的情况答案一定也不变。

考虑到 \(p_{k+1}-p_k\) 代表的就是斜率,即斜率单调非降。

那么图象就是个下凸壳了,可以使用 wqs 二分进行求解。


假设当前二分的斜率为 \(\text{mid}\)

\(f_i\) 表示覆盖到第 \(i\) 条线段的最小代价。

\[f_i=-\text{mid}+\min\limits_{j=0}^{i-1}\begin{cases}f_j+(r_i-l_{j+1}+1)^2-(r_j-l_{j+1}+1)^2 & r_j \ge l_{j+1} \wedge j>0 \\ f_j+(r_i-l_{j+1}+1)^2 & \text{otherwise} \end{cases} \]

\(r_i \ge l_{i+1}\),可以使用一个 \(g_i\) 表示 \((r_i-l_{i+1}+1)^2\);否则 \(g_i=0\)

观察到含 \(i\) 的项和含 \(j\) 的项相乘,考虑斜率优化。

\(0 \le j_1 < j_2 < i\),且由 \(j_2\) 转移过来比 \(j_1\) 更优。

那么有。

\[f_{j_2}+(r_i-l_{j_2+1}+1)^2-g_{j_2} \le f_{j_1}+(r_i-l_{j_1+1}+1)^2-g_{j_1} \]

发现这个 \(r_i+1\) 很烦人,那么在预处理的时候就把 \(r_i\) 加上 \(1\)

\[f_{j_2}+(r_i-l_{j_2+1})^2-g_{j_2} \le f_{j_1}+(r_i-l_{j_1+1})^2-g_{j_1} \]

把式子拆开。

\[f_{j_2}+r_i^2-2r_i\times l_{j_2+1}+l_{j_2+1}^2-g_{j_2} \le f_{j_1}+r_i^2-2r_i\times l_{j_1+1}+l_{j_1+1}^2-g_{j_1} \]

抵消后,把含 \(i\) 的式子丢到左边,其余丢到右边。

\[2r_i \times (l_{j_1+1}-l_{j_2+1}) \le (f_{j_1}+l_{j_1+1}^2-g_{j_1})-(f_{j_2}+l_{j_2+1}^2-g_{j_2}) \]

由于 \(l\) 单调递增,故 \(l_{j_1+1}-l_{j_2+1}<0\),丢到右边要变号。

\[2r_i \ge \dfrac{(f_{j_1}+l_{j_1+1}^2-g_{j_1})-(f_{j_2}+l_{j_2+1}^2-g_{j_2})}{l_{j_1+1}-l_{j_2+1}} \]

因为 \(j_1<j_2\),换成正常的斜率形式。

\[2r_i \ge \dfrac{(f_{j_2}+l_{j_2+1}^2-g_{j_2})-(f_{j_1}+l_{j_1+1}^2-g_{j_1})}{l_{j_2+1}-l_{j_1+1}} \]

由于 \(r\) 单调递增,故斜率单调递增,用单调队列维护下凸壳即可。

由于 wqs 二分要记录当前选的区间个数,所以还要在答案最小的情况下,最小化选择的区间个数。此时可以加强判定条件,把弹队头的 \(\ge\) 换成 \(>\),保证当前答案从更早的地方转移过来,一定更优。

时间复杂度 \(\mathcal{O}(n \log v)\)\(v\) 表示斜率的值域,即最大答案 \(m^2\)

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 1e5 + 10;
int n, m, k, cnt, res, f[N], g[N], q[N], dp[N];

struct Node {
    int l, r;
    inline bool operator <(const Node &X) const {
        if (l == X.l) return r > X.r;
        return l < X.l;
    }
} p[N];

inline int pf(int x) { return x * x; }
inline int getx(int j1, int j2) { return p[j2 + 1].l - p[j1 + 1].l; }
inline int gety(int j1, int j2) {
	int y = f[j1] + pf(p[j1 + 1].l) - g[j1];
    int _y = f[j2] + pf(p[j2 + 1].l) - g[j2];
    return _y - y;
}

inline bool check(int mid) {
    int l, r; q[l = r = 1] = 0;
    for (int i = 1; i <= n; ++i) {
        while (l < r && 2 * getx(q[l], q[l + 1]) * p[i].r > gety(q[l], q[l + 1])) ++l;
        f[i] = f[q[l]] + pf(p[i].r - p[q[l] + 1].l) - g[q[l]] - mid, dp[i] = dp[q[l]] + 1;
        while (l < r && gety(q[r - 1], q[r]) * getx(q[r], i) > gety(q[r], i) * getx(q[r - 1], q[r])) --r;
        q[++r] = i;
    }
    return dp[n] <= k;
}

signed main() {
   	ios_base::sync_with_stdio(false); cin.tie(0), cout.tie(0);
   	cin >> n >> m >> k;
    for (int i = 1; i <= n; ++i) {
        cin >> p[i].l >> p[i].r;
        if (p[i].l > p[i].r) swap(p[i].l, p[i].r);
    }
    sort(p + 1, p + 1 + n);
    for (int i = 1, r = -1; i <= n; ++i)
        if (p[i].r > r) r = p[i].r, p[++cnt] = p[i]; n = cnt;
    for (int i = 1; i < n; ++i) if (p[i].r >= p[i + 1].l)
        g[i] = pf(p[i].r - p[i + 1].l + 1);
    for (int i = 1; i <= n; ++i) ++p[i].r;
    int l = -m * m, r = 0, res = 0;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) res = f[n] + k * mid, l = mid + 1;
        else r = mid - 1;
    }
    return cout << res << endl, 0;
}
posted @ 2023-03-15 00:12  MistZero  阅读(28)  评论(0编辑  收藏  举报