bzoj 4082 [Wf2014]Surveillance 倍增

本文介绍了一个将环形问题转化为链式问题的算法思路,通过倍增技巧找到能够完全覆盖给定长度的区间组合。文章详细解释了如何构建区间关系树,并利用倍增法求解最小覆盖区间的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面

题目传送门

解法

不妨将环变成链

那么就变成选出若干段区间,使得它们中间没有空隙且长度≥len

将所有区间按照右端点排序

那么,对于每一个区间,我们都可以找到右端点最大的且能和该区间连接的区间

然后发现这些关系形成了一棵树

那么,现在我们枚举最左边的区间,倍增找到最近的且能完全覆盖这个环的区间

原题中在无解的情况下需要输出impossible,然而题目并没有显示

时间复杂度:\(O(n\ log\ n)\)

代码

#include <bits/stdc++.h>
#define N 1000010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {
    int l, r;
    bool operator < (const Node &a) const {
        return r < a.r;
    }
} a[N];
int d[N], mx[N], f[N][21];
void getd(int x) {
    if (!f[x][0]) d[x] = 1;
    if (d[x]) return;
    getd(f[x][0]); d[x] = d[f[x][0]] + 1;
}
int main() {
    int n, m; read(m), read(n);
    for (int i = 1; i <= n; i++) {
        read(a[i].l), read(a[i].r);
        if (a[i].l > a[i].r) a[i].r += m;
    }
    sort(a + 1, a + n + 1);
    for (int i = 1; i <= n; i++) chkmax(mx[a[i].l], i);
    int tmp = 0;
    for (int i = 1, j = 1; i <= n; i++) {
        for (; j <= a[i].r + 1; j++)
            chkmax(tmp, mx[j]);
        f[i][0] = (tmp == i) ? 0 : tmp;
    }
    for (int i = 1; i <= n; i++) getd(i);
    for (int j = 1; j <= 20; j++)
        for (int i = 1; i <= n; i++)
            f[i][j] = f[f[i][j - 1]][j - 1];
    int ans = 1 << 30;
    for (int i = 1; i <= n; i++) {
        int x = i;
        for (int j = 20; j >= 0; j--)
            if (a[f[x][j]].r - a[i].l + 1 < m && f[x][j]) x = f[x][j];
        if (a[x].r - a[i].l + 1 < m) x = f[x][0];
        if (a[x].r - a[i].l + 1 >= m) chkmin(ans, d[i] - d[x] + 1);
    }
    if (ans == (1 << 30)) cout << "impossible\n";
        else cout << ans << "\n";
    return 0;
}

转载于:https://www.cnblogs.com/copperoxide/p/9476482.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值