P5896 Aliens Sol

终于写了这题!

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

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

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

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

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

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

  • 结合第一点与第二点,我们发现排序后,需要选择的区间显然满足 li1<li,ri1<ri
  • wqs 二分代表的实际上是一个斜率始终 0 的下凸壳。

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

pk 表示强制选择 k 个区间时的答案。

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

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

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

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

考虑到 pk+1pk 代表的就是斜率,即斜率单调非降。

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


假设当前二分的斜率为 mid

fi 表示覆盖到第 i 条线段的最小代价。

fi=mid+minj=0i1{fj+(rilj+1+1)2(rjlj+1+1)2rjlj+1j>0fj+(rilj+1+1)2otherwise

rili+1,可以使用一个 gi 表示 (rili+1+1)2;否则 gi=0

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

0j1<j2<i,且由 j2 转移过来比 j1 更优。

那么有。

fj2+(rilj2+1+1)2gj2fj1+(rilj1+1+1)2gj1

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

fj2+(rilj2+1)2gj2fj1+(rilj1+1)2gj1

把式子拆开。

fj2+ri22ri×lj2+1+lj2+12gj2fj1+ri22ri×lj1+1+lj1+12gj1

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

2ri×(lj1+1lj2+1)(fj1+lj1+12gj1)(fj2+lj2+12gj2)

由于 l 单调递增,故 lj1+1lj2+1<0,丢到右边要变号。

2ri(fj1+lj1+12gj1)(fj2+lj2+12gj2)lj1+1lj2+1

因为 j1<j2,换成正常的斜率形式。

2ri(fj2+lj2+12gj2)(fj1+lj1+12gj1)lj2+1lj1+1

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

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

时间复杂度 O(nlogv)v 表示斜率的值域,即最大答案 m2

#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 @   MistZero  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示