首页 > 其他分享 >CF793F Julia the snail 题解

CF793F Julia the snail 题解

时间:2023-08-14 19:23:32浏览次数:28  
标签:CF793F right snail 题解 valueType 端点 max id left

题意

有一个长为 \(n\) 的杆,上面有 \(m\) 条绳子,每条绳子可以让蜗牛从 \(l_i\) 爬到 \(r_i\)(中途不能离开),保证 \(r_i\) 各不相同。蜗牛也可以自然下落。

现在有 \(q\) 次询问,询问 \(x\) 出发,途中高度不能低于 \(x\) 或高于 \(y\),问最高能爬到的位置。

\(n,m,q\leq 10^5\)。

题解

模拟赛 T4,加强了数据范围,改为了 \(n,m,q\leq 10^6\),卡掉了分块做法。

我们考虑将询问离线。因为题目中明确不同绳子的 \(r_i\) 互不相同,所以我们考虑按询问的右端点去处理,维护当前左端点的答案。换句话说,我们使用当前处理的区间的右端点 \(r\) 去遍历,并维护所有满足 \(l \in \left[1,r\right]\) 的区间 \(\left[l,r\right]\) 的答案。

考虑我们移动了右端点 \(r\),即 \(r - 1 \rightarrow r\),可以发现只有存在右端点为 \(r\) 的绳子,那么答案才会产生相应的影响。那么我们考虑存在右端点为 \(r\) 的绳子,考虑其对答案的影响,我们设这条绳子的左端点为 \(l\),设对于\(x \in \left[1,r\right]\),\(ans_x\) 为左端点为 \(x\),右端点为 \(r\) 的区间的询问的答案。也就是从 \(x\) 出发,只使用当前已经添加了的绳子,可以爬到的最高高度。我们考虑添加绳子对答案的影响,如果 \(x > l\),根据题意,由于其只能使用包含在区间 \(\left[x,r\right]\) 内的绳子,所以这条绳子不会对这部分左端点的答案产生影响。对于 \(x \le l\),若 \(ans_x < l\),由于蜗牛无法爬到这条绳子的最低点,所以也不会产生影响。所以这条绳子只对满足 \(x \in \left[1,l\right] \land ans_x \ge l\) 的左端点产生影响,不难看出会把这部分左端点的答案直接更新为 \(r\)。

考虑使用数据结构维护对答案的影响,我们需要一个数据结构支持将一定区间内大于等于给定值的部分更新为一个新的更大的值。可以想到吉司机线段树可以满足这个需求,实现更改。具体实现的话我们可以对于每个线段树节点维护最大值 \(max_i\) 和次大值 \(sec_i\),对于每次更改,可以表达为四元组 \(\left(l, r, A, B\right)\) 的形式,即将区间 \(\left[l,r\right]\) 的大于等于 \(A\) 的部分更新为 \(B\)。对于更改的时候,如果 \(max_i < A\),那么这个区间的值不会被更新,如果 \(max_i \ge A > sec_i\),那么只更新最大值即可,如果 \(sec_i \ge A\),那么递归地处理。在 \(n,m,q\) 同阶的情况下,总复杂度为 \(\mathcal{O}(n \log n)\)。

复杂度分析

这部分是笔者自己对吉司机线段树的理解,如有错误欢迎各位指正。

考虑所有满足 \(a_i \neq a_{i + 1}\) 的点对 \(\left(i, i + 1\right)\),如果我们的更改操作递归到了叶子节点,那么这样的点对便会减少一个,可以看出每消除一个这样的点对复杂度均为 \(\mathcal{O}(\log n)\),初始状态下这样的点对有 \(\mathcal{O}(n)\) 个(线段树初始化时 \(ans_x \leftarrow x\)),考虑每次更改对点对数量的影响,可以看出最多会增加两个点对 \(\left(l - 1,l\right)\) 和 \(\left(r, r + 1\right)\),所以总共出现过的的点对数量为 \(\mathcal{O}(n + q)\),又因为消除每个点对的复杂度均为 \(\mathcal{O}(\log n)\),所以对于操作的三种情况中最后一种情况的复杂度为 \(\mathcal{O}(n \log n)\),其他情况和单点查询的复杂度同普通线段树,故总复杂度为 \(\mathcal{O}(n \log n)\)。

Code

//D
#include <bits/stdc++.h>

typedef int valueType;
typedef std::vector<valueType> ValueVector;

constexpr valueType MIN = INT_MIN;

class SegmentBeats {
public:
    typedef ValueVector container;

private:
    valueType N;

    container max, second, tagA, tagB;

    void addTag(valueType id, valueType A, valueType B) {
        if (max[id] >= A && max[id] <= B) {
            max[id] = std::max(max[id], B); //TAG

            if (tagA[id] == MIN) //TAG
                tagA[id] = A;

            tagB[id] = B;
        }
    }

    void pushDown(valueType id) {
        if (tagA[id] != MIN) {
            addTag(id << 1, tagA[id], tagB[id]);
            addTag(id << 1 | 1, tagA[id], tagB[id]);

            tagA[id] = tagB[id] = MIN;
        }
    }

    void update(valueType id, valueType key) {
        if (key > max[id]) {
            second[id] = max[id];
            max[id] = key;
        } else if (key < max[id] && key > second[id]) {
            second[id] = key;
        }
    }

    void pushUp(valueType id) {
        max[id] = MIN;
        second[id] = MIN;

        update(id, max[id << 1]);
        update(id, second[id << 1]);
        update(id, max[id << 1 | 1]);
        update(id, second[id << 1 | 1]);
    }

public:
    explicit SegmentBeats(valueType size) : N(size), max((N << 2) + 5, MIN), second((N << 2) + 5, MIN),
                                            tagA((N << 2) + 5, MIN), tagB((N << 2) + 5, MIN) {
        build(1, 1, N);
    }

    void modify(valueType L, valueType R, valueType A, valueType B) {
        modify(1, 1, N, L, R, A, B);
    }

    valueType query(valueType pos) {
        return query(1, 1, N, pos);
    }

private:
    void build(valueType id, valueType L, valueType R) {
        if (L == R) {
            max[id] = L;
            return;
        }

        valueType mid = (L + R) >> 1;

        build(id << 1, L, mid);
        build(id << 1 | 1, mid + 1, R);

        pushUp(id);
    }

    void modify(valueType id, valueType nodeL, valueType nodeR, valueType queryL, valueType queryR, valueType A,
                valueType B) {
        if (max[id] < A)
            return;

        if (nodeL >= queryL && nodeR <= queryR && second[id] < A) {
            addTag(id, A, B);

            return;
        }

        pushDown(id);

        valueType mid = (nodeL + nodeR) >> 1;

        if (queryL <= mid)
            modify(id << 1, nodeL, mid, queryL, queryR, A, B);

        if (queryR > mid)
            modify(id << 1 | 1, mid + 1, nodeR, queryL, queryR, A, B);

        pushUp(id);
    }

    valueType query(valueType id, valueType nodeL, valueType nodeR, valueType pos) {
        if (nodeL == nodeR)
            return max[id];

        pushDown(id);

        valueType mid = (nodeL + nodeR) >> 1;

        if (pos <= mid)
            return query(id << 1, nodeL, mid, pos);
        else
            return query(id << 1 | 1, mid + 1, nodeR, pos);
    }
};

struct Query {
    valueType left;
    valueType id;

    Query() = default;

    Query(valueType left, valueType id) : left(left), id(id) {};
};

int main() {
    std::ios_base::sync_with_stdio(false);

    valueType N, M;

    std::cin >> N >> M;

    ValueVector rope(N + 1, 0);

    for (valueType i = 0; i < M; ++i) {
        valueType left, right;

        std::cin >> left >> right;

        rope[right] = left;
    }

    SegmentBeats tree(N);

    valueType Q;

    std::cin >> Q;

    std::vector<std::vector<Query>> query(N + 1);

    for (valueType i = 0; i < Q; ++i) {
        valueType left, right;

        std::cin >> left >> right;

        query[right].emplace_back(left, i);
    }

    ValueVector ans(Q);

    for (valueType i = 1; i <= N; ++i) {
        valueType left = rope[i];
        valueType right = i;

        if (left > 0)
            tree.modify(1, left, left, right);


        for (auto const &iter: query[right])
            ans[iter.id] = tree.query(iter.left);
    }

    for (auto const &iter: ans)
        std::cout << iter << '\n';

    return 0;
}

标签:CF793F,right,snail,题解,valueType,端点,max,id,left
From: https://www.cnblogs.com/User-Unauthorized/p/solution-cf793f.html

相关文章

  • 【免费分享 图书】《阿里云天池大赛赛题解析——机器学习篇》-PDF电子书-百度云
    找这本书的资源简直要把我找吐了,各种网站压缩包一下下来就开始各种套路(比如要你充钱)为了防止还有我这样的受害者,这就把找到的PDF给大家分享一下。链接在文章最后如果这篇文章能够帮到您,麻烦帮我点个赞,并关注一下我,我有更多动力,持续分享更多有用图书给您!非常感谢,不胜感激!(点关......
  • 「题解注释」P3345 [ZJOI2015] 幻想乡战略游戏
    题解P3345【[ZJOI2015]幻想乡战略游戏】-Baka'sBlog-洛谷博客(luogu.org)耗时:半个下午代码注释:#include<bits/stdc++.h>typedeflonglongLL;inlineintrd(){ inta=1,b=0;charc=getchar(); while(!isdigit(c))a=c=='-'?0:1,c=getcha......
  • CF1859C 题解
    思路我们实际上发现它计算的就是\(p_i\cdoti\)的和再减去一个\(p_i\cdoti\)中的最大值。那我们可以枚举这个最大值\(p_x\cdotx\),这个值就是最后和中需要删除的数值。这里我们可以使用贪心。我们可以从\(n\sim1\)枚举除\(p_i\)的每个数字需要配的数字。当然,......
  • CF1859B 题解
    题意给定\(n\)个长度为\(m\)的数组,每个数组可以向别的数组转移最多一个数字,任意一个数组都可以接受无穷多的数字,最大化每个数组的最小值之和。做法考虑贪心。我们记第\(i\)个数组的第\(j\)个数字为\(a_{i,j}\)。我们先对每一个数组按照升序进行排序,那我们最不愿意......
  • 【题解】 Call Me Call Me CCPC Mianyang 2022
    https://codeforces.com/gym/104065/原题做法是类似猫树转成前缀后缀,写起来太麻烦,不如如下做法:如果每个区间所需满足的点不超过\(\sqrt{n}\)个,即可以如下暴力:把每个区间拍到线段树上,每次更新一个点,则在线段树上把所有包含他的区间全部\(-1\)看看是否减到了\(0\),拿个队列一......
  • 问题解答:关于 SAP UI5 控制器(Controller) JavaScript 编码里单引号和双引号的用法澄
    笔者这篇教程文末,有朋友提问:SAPUI5应用开发教程之十-什么是SAPUI5应用的描述符文件manifest.json问题1:在index.html文件中body标签添加了代码:<divdata-sap-ui-componentdata-name="sap.ui5.walkthrough"data-id="container"data-settings='{"id":"wa......
  • ARC129C 题解
    problem&blog。提供一种不一样的做法喵。考虑原问题的逆问题。这个很典,直接前缀和\(sum_i\)表示\([1,i]\)顺次拼接的数\(\bmod7\)的值,那么\([l,r]\)符合条件当且仅当\(sum_r-sum_{l-1}=0\),即\(sum_r=sum_{l-1}\)。设\(c_p=\sum\limits_{i=1}^n[sum_i=p]\),这个逆......
  • 【题解】洛谷 P9532 [YsOI2023] 前缀和
    原题链接【LGR-151-Div.2】洛谷8月月赛II&YsOI2023T1解题思路设有一序列a,其中a1 =a2,第k(≥3) 项为前k-1项的前缀和。可以发现前q项分别为第一项的20 倍,20 倍,21 倍,22 倍,23 倍…2q-3 倍,2q-2 倍。扩展到整个序列中,可得第i( ≥ 3)项一定为2i-2a1。......
  • 【LSOIT1】秒速,五厘米 ----题解
    【LSOIT1】秒速,五厘米----题解题目传送门【明里。】【贵树君。】【明年,也能一起看樱花吗?】【昨天,我做了一个梦,在梦里,我们都才十三岁。那是覆盖着厚厚的一层白雪的田园。】【民家的灯火遥不可及,只能看到零星的两点。】很显然,这是一道签到题,出题人非常良心。题目大意:从......
  • 【LSOIT2】言叶之庭 ---题解
    【LSOIT2】言叶之庭---题解题目传送门【你肯定怀疑我有问题吧。】【没有。】【我不介意呀,反正人类,多多少少有点不正常的。】【我知道这不正常,但真的很喜欢设计鞋子,当然,水平还不够。】【不知不觉,我都在期待雨天。晴天里,总觉得被困在孩子气里的世界,焦虑无比。】【在我看来......