Codeforces 1080F - Katya and Segments Sets - 可持久化线段树

140 篇文章 0 订阅
80 篇文章 0 订阅

Codeforces 1080F - Katya and Segments Sets - 可持久化线段树

题解链接

https://lucien.ink


题目链接

https://codeforces.com/contest/1080/problem/F


题目

It is a very important day for Katya. She has a test in a programming class. As always, she was given an interesting problem that she solved very fast. Can you solve that problem?

You are given n n n ordered segments sets. Each segment can be represented as a pair of two integers [ l , r ] [l, r] [l,r] where l ≤ r l\leq r lr. Each set can contain an arbitrary number of segments (even 0 0 0). It is possible that some segments are equal.

You are also given m m m queries, each of them can be represented as four numbers: a , b , x , y a, b, x, y a,b,x,y. For each segment, find out whether it is true that each set p p p ( a ≤ p ≤ b a\leq p\leq b apb) contains at least one segment [ l , r ] [l, r] [l,r] that lies entirely on the segment [ x , y ] [x, y] [x,y], that is x ≤ l ≤ r ≤ y x\leq l\leq r\leq y xlry.

Find out the answer to each query.

Note that you need to solve this problem online. That is, you will get a new query only after you print the answer for the previous query.


题意

  你有 n n n 个集合, k k k 条线段,每条线段都只属于一个集合,集合的编号为 1 … n 1 \dots n 1n,允许存集合为空(为了叙述方便,在这里我们将编号为 i i i 的集合中的第 j j j 条线段简记为 s e g [ i ] [ j ] seg[i][j] seg[i][j])。有 m m m 次询问,每次询问有四个值 a b x y,问:

  • ∀   i ∈ [ a , b ] \forall\ i \in [a, b]  i[a,b]
  • ∃   j \exists\ j  j,使得 s e g [ i ] [ j ] ⊆ s e g n m e n t [ x , y ] seg[i][j] \subseteq segnment[x, y] seg[i][j]segnment[x,y]

  是否成立。


思路

  先从忽略时间、空间复杂度的角度去考虑问题。

  对于单个集合,考虑如何快速的求解答案。我们可以借助动态规划的思想,处理出左端点坐标大于等于 x x x 的所有线段的右端点的最小值 f [ x ] f[x] f[x],这样对于单次询问 x y,只需要判断 f [ x ] ≤ y f[x] \leq y f[x]y 是否成立即可。

  对于多个集合,我们将每个集合的 f f f 函数都预处理出来,记 f [ i ] [ x ] f[i][x] f[i][x] 为编号为 i i i 的集合中左端点坐标大于等于 x x x 的所有线段的右端点的最小值,对于单次询问 a b x y,我们只需要判断 m a x { f [ a ] [ x ] , f [ a + 1 ] [ x ] … f [ b ] [ x ] } ≤ y max\{f[a][x], f[a + 1][x] \dots f[b][x]\} \leq y max{f[a][x],f[a+1][x]f[b][x]}y 是否成立即可。

  这样一来,我们就从逻辑上得出了一种看似可行的做法,开始考虑优化。

  对于单次询问 a b x y,我们判断 m a x { f [ a ] [ x ] , f [ a + 1 ] [ x ] … f [ b ] [ x ] } ≤ y max\{f[a][x], f[a + 1][x] \dots f[b][x]\} \leq y max{f[a][x],f[a+1][x]f[b][x]}y 是否成立时,可以观察到, m a x { f [ a ] [ x ] … f [ b ] [ x ] } max\{f[a][x] \dots f[b][x]\} max{f[a][x]f[b][x]} 可以通过维护一颗叶节点 i i i 代表 f [ i ] [ x ] f[i][x] f[i][x] 的最大值线段树来在 O ( l o g 2 ( n ) ) O(log_2(n)) O(log2(n)) 的时间内得到,可每次询问的时候,我都需要将线段树先清空,再将所有涉及到询问的集合中左端点的坐标大于等于 x x x 的线段都一一重新插入,此时单次询问的复杂度为 O ( n ⋅ l o g 2 ( n ) ) O(n \cdot log_2(n)) O(nlog2(n))

  优化重构问题,显然需要用到可持久化数据结构,也就是可持久化线段树了。

  我们将所有线段都按左端点进行一次从大到小的 s o r t sort sort,右端点可以忽略,我们按照左端点从大到小的顺序将右端点插到可持久化线段树中,这样一来我们对某个拷贝进行查询的时候,就可以得到 m a x { f [ a ] [ x ] … f [ b ] [ x ] } max\{f[a][x] \dots f[b][x]\} max{f[a][x]f[b][x]} 的值了,将其与 y y y 作比较即可。单次询问复杂度为 O ( l o g 2 ( n ) ) O(log_2(n)) O(log2(n))


实现

https://pasteme.cn/2341

#include <bits/stdc++.h>
const int maxn = int(1e5) + 7, inf = 0x3f3f3f3f;
int n, m, k, key[maxn << 2], value[maxn << 2], len;
struct SegmentTree {
    struct Node {
        int val, l, r;
        Node():val(inf), l(0), r(0) {}
    } node[maxn << 6];
    int root[maxn << 2], size, cnt;
    SegmentTree():size(0), cnt(0) {}
    void pushup(int cur) {
        node[cur].val = std::max(node[node[cur].l].val, node[node[cur].r].val);
    }
    void update(int pos, int val, int &cur, int pre, int l, int r) {
        node[cur = ++size] = node[pre];
        if (l == r) {
            node[cur].val = std::min(node[cur].val, val);
            return ;
        }
        int mid = l + r >> 1;
        if (pos <= mid) update(pos, val, node[cur].l, node[pre].l, l, mid);
        else update(pos, val, node[cur].r, node[pre].r, mid + 1, r);
        pushup(cur);
    }
    int query(int ql, int qr, int cur, int l, int r) {
        if (cur == 0) return inf;
        if (ql <= l && r <= qr) return node[cur].val;
        int mid = l + r >> 1, ret = 0;
        if (ql <= mid) ret = std::max(ret, query(ql, qr, node[cur].l, l, mid));
        if (qr > mid) ret = std::max(ret, query(ql, qr, node[cur].r, mid + 1, r));
        return ret ? ret : inf;
    }
    void update(int pos, int val) {
        ++cnt;
        update(pos, val, root[cnt], root[cnt - 1], 1, n);
    }
    int query(int l, int r, int cur) {
        return query(l, r, root[cur], 1, n);
    }
} htj;
struct Segment { int l, r, p; } segment[maxn << 2];
int main() {
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= k; i++) scanf("%d%d%d", &segment[i].l, &segment[i].r, &segment[i].p);
    std::sort(segment + 1, segment + 1 + k, [](Segment x, Segment y) { return x.l == y.l ? x.r > y.r : x.l > y.l; });
    for (int i = 1; i <= k; i++) {
        if (segment[i].l != segment[i - 1].l) key[++len] = segment[i].l;
        value[len] = i;
        htj.update(segment[i].p, segment[i].r);
    }
    for (int i = 1, a, b, x, y; i <= m; i++) {
        scanf("%d%d%d%d", &a, &b, &x, &y);
        int pos = value[int(std::upper_bound(key + 1, key + 1 + len, x, std::greater<int>()) - key) - 1];
        puts(htj.query(a, b, pos) <= y ? "yes" : "no");
        fflush(stdout);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值