zoukankan      html  css  js  c++  java
  • 题解 洛谷 P3332

    题目描述

    权值线段树套线段树板子题

    首先观察题目,判断为二维偏序问题

    操作1为区间修改,所以一定是外部线段树维护权值,内部线段树维护所在区间,否则时间复杂度爆炸qwq

    为方便查找,哈希时我采用哈希每个数的相反数的方法将求第k大转换为求第k小

    询问可以直接想到的做法就是二分答案,查询1~ans在区间内的个数,时间复杂度 O(nlog^3n)

    尝试去掉一个log,思考发现可以直接在权值线段树上二分,每次查询左子树表示的数在区间内的个数p,若p大于等于当前查询的第k小则直接进入左子树,否则进入右子树并将k减p。时间复杂度O(nlog^2n)

    内部线段树采用动态开点,空间复杂度O(nlog^2n) (n、m同阶)

    但是我第一次写的树套树貌似常数过大,T了7个点,于是又加上了标记永久化,省去了标记下放的时间和不必要的空间浪费。区间修改时直接在经过的节点上修改权值并在原先打标记的节点上打上永久化标记,查询是将经过的点的标记都加起来乘区间长度再加上询问区间的权值即为答案。

    具体实现见代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #define gc getchar
    #define re register
    using namespace std;
    template <typename T> void rd(T& s)
    {
        s = 0;
        bool p = 0;
        char ch;
        while (ch = gc(), p |= ch == '-', ch < '0' || ch > '9');
        while (s = s * 10 + ch - '0', ch = gc(), ch >= '0' && ch <= '9');
        s *= (p ? -1 : 1);
    }
    template <typename T, typename... Args> void rd(T& s, Args&... args)
    {
        rd(s);
        rd(args...);
    }
    int cnt, tot;
    #define LL long long
    #define new_node(ls, rs, val) (st[++cnt] = node(ls, rs, val), cnt)
    const int MAXN = 50050;
    const int MAX = 50000000;
    struct node
    {
        int ls, rs;
        LL val;
        int tag;
        node(int ls, int rs, LL val) : ls(ls), rs(rs), val(val), tag(0) {}
        node() {}
    }st[MAX];
    struct que
    {
        int opt, a, b;
        LL c;
        que(int opt, int a, int b, LL c) : opt(opt), a(a), b(b), c(c) {}
        que() {}
    }qu[MAXN];
    int n;
    LL hs[MAXN];
    int tree[MAXN << 2];
    void modify(int& o, re int l, re int r, re int ll, re int rr)
    {
        if (!o)
            o = new_node(0, 0, 0);
        st[o].val += (r - l + 1);
        if (l == ll && r == rr)
        {
            st[o].tag += 1;
            return;
        }
        re int mid = (ll + rr) >> 1;
        if (r <= mid)
            modify(st[o].ls, l, r, ll, mid);
        else
            if (l > mid)
                modify(st[o].rs, l, r, mid + 1, rr);
            else
                modify(st[o].ls, l, mid, ll, mid),
                modify(st[o].rs, mid + 1, r, mid + 1, rr);
    }
    LL query(re int o, re int l, re int r, re int ll, re int rr, re int tag)
    {
        if (!o)
            return tag * 1ll * (r - l + 1);
        if (l == ll && r == rr)
            return st[o].val + tag * 1ll * (r - l + 1);
        re int mid = (ll + rr) >> 1;
        if (r <= mid)
            return query(st[o].ls, l, r, ll, mid, tag + st[o].tag);
        else
            if (l > mid)
                return query(st[o].rs, l, r, mid + 1, rr, tag + st[o].tag);
            else
                return query(st[o].ls, l, mid, ll, mid, tag + st[o].tag) + query(st[o].rs, mid + 1, r, mid + 1, rr, tag + st[o].tag);
    }
    void add(re int o, re int k, re int l, re int r, re int ll, re int rr)
    {
        modify(tree[o], l, r, 1, n);
        if (ll == rr)
            return;
        re int mid = (ll + rr) >> 1;
        if (k <= mid)
            add(o << 1, k, l, r, ll, mid);
        else
            add((o << 1) | 1, k, l, r, mid + 1, rr);
    }
    int midsearch(re int o, re int ll, re int rr, re int l, re int r, re int k)
    {
        if (ll == rr)
            return ll;
        re LL p = query(tree[o << 1], l, r, 1, n, 0);
        if (p >= k)
            return midsearch(o << 1, ll, (ll + rr) >> 1, l, r, k);
        else
            return midsearch((o << 1) | 1, ((ll + rr) >> 1) + 1, rr, l, r, k - p);
    }
    int main()
    {
        re int m = 0, opt, a, b;
        re LL c;
        rd(n, m);
        for (re int i = 1; i <= m; ++i)
        {
            rd(opt, a, b);
            rd(c);
            qu[i] = que(opt, a, b, c);
            if (opt == 1)
                hs[++tot] = -c;
        }
        sort(hs + 1, hs + 1 + tot);
        tot = unique(hs + 1, hs + 1 + tot) - hs - 1;
        for (re int i = 1; i <= m; ++i)
        {
            if (qu[i].opt == 1)
            {
                add(1, lower_bound(hs + 1, hs + 1 + tot, -qu[i].c) - hs, qu[i].a, qu[i].b, 1, tot);
            }
            else
            {
                printf("%lld
    ", -hs[midsearch(1, 1, tot, qu[i].a, qu[i].b, qu[i].c)]);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    MySQL中TIMESTAMP和DATETIME区别
    图片标签的alt与title区别
    DEDE自带的采集功能,标题太短的解决方法
    Modernizr——为HTML5和CSS3而生!
    InnoDB,MyISAM,Memory区别
    Innodb,MyIsam,聚集索引和非聚集索引
    聚集索引与非聚集索引的总结
    程序kill -9与kill -15的区别,以及回调函数的作用
    linux 信号 SIGINT SIGTERM SIGKILL区别
    oracle mysql sqlserver 查看当前所有数据库及数据库基本操作命令
  • 原文地址:https://www.cnblogs.com/happyLittleRabbit/p/10392238.html
Copyright © 2011-2022 走看看