题解 洛谷 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\n", -hs[midsearch(1, 1, tot, qu[i].a, qu[i].b, qu[i].c)]);
        }
    }
    return 0;
}
posted @ 2019-02-17 18:56  Ηydra  阅读(183)  评论(0编辑  收藏  举报