[Luogu P3332] [BZOJ][ZJOI2013]K大数查询

BZOJ传送门
洛谷传送门

题目描述

N N 个位置,M个操作。操作有两种,每次操作如果是 1 a b c 1   a   b   c 的形式表示在第 a a 个位置到第b个位置,每个位置加入一个数 c c 如果是2 a b c形式,表示询问从第 a a 个位置到第b个位置,第 C C 大的数是多少。

输入输出格式

输入格式:

第一行N M M 。接下来M行,每行形如 1 a b c 1   a   b   c 2 a b c 2   a   b   c

输出格式:

输出每个询问的结果

输入输出样例

输入样例#1:
2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
输出样例#1:
1
2
1
说明

【样例说明】

第一个操作后位置 1 1 的数只有 1 , 位置 2 2 的数也只有 1 。 第二个操作 后位置 1的数有 1 1 2 ,位置 2 2 的数也有 1 2 2 。 第三次询问 位置 1 到位置 1 1 2 大的数 是 1 1 。 第四次询问 位置 1 到位置 1 1 1 大的数是 2 2 。 第五次询问 位置 1 到位置 2 2 3大的数是 1 1 。‍

N,M50000,N,M50000
abN a ≤ b ≤ N
1操作中 abs(c)N a b s ( c ) ≤ N
2操作中 c<=long long c <= l o n g   l o n g

解题分析

这是一道经典的树套树的模板题, 也可以用整体二分做(待填坑)。
显然我们需要维护两个信息:询问区间的总信息和区间内权值的信息。所以我们外层使用权值线段树表示区间 [ql,qr] [ q l , q r ] 的信息(可以不用建出来,只是指向内层线段树),内层使用普通线段树维护在 [ql,qr] [ q l , q r ] 范围内的值在序列中的分布情况。这样修改时相当于单点修改,会在权值线段树上跳 log(N) l o g ( N ) 层, 同时会造成内层线段树上每层 log(N) l o g ( N ) 个修改节点。查询时在外层上二分答案, 同时进入内层相应区间计算即可。总复杂度 Nlog2(N) N l o g 2 ( N ) 。但蒟蒻代码常数太大, 会T3个点QAQ

代码如下:

// luogu-judger-enable-o2
#include <cstdio>
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cmath>
#include <cstring>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MX 100050
#define ll long long
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define lss now << 1
#define rss now << 1 | 1
bool neg;
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    W (!isdigit(c)) {if(c == '-') neg = true; c = gc;}
    W (isdigit(c))
    x = (x << 1) + (x << 3) + c - 48, c = gc;
    if(neg) neg = false, x = -x;
}
int dot, q, cnt, dif, rt, bcnt;
struct Node
{
    int son[2];
    ll sum, lazy;
}tree[MX * 200];
struct OP
{
    int typ, lef, rig;
    ll kth;
}op[MX];
int dat[MX], buf[MX], root[MX << 4];
namespace PT
{
    IN void pushup(const int &now) {tree[now].sum = tree[ls].sum + tree[rs].sum;}
    IN void pushdown(const int &now, const int &lef, const int &rig)
    {
        int mid = lef + rig >> 1;
        if(tree[now].lazy)
        {
            if(!ls) ls = ++cnt;
            if(!rs) rs = ++cnt;
            tree[rs].sum += 1ll * tree[now].lazy * (rig - mid);
            tree[ls].sum += 1ll * tree[now].lazy * (mid - lef + 1);
            tree[ls].lazy += tree[now].lazy, tree[rs].lazy += tree[now].lazy;
            tree[now].lazy = 0;
        }
    }
    void add_in(int &now, const int &lef, const int &rig, const int &lb, const int &rb)
    {
        if(!now) now = ++cnt;
        if(lb <= lef && rb >= rig)
        {
            tree[now].sum += rig - lef + 1;
            ++tree[now].lazy;
            return;
        }
        pushdown(now, lef, rig);//注意随时pushdown
        int mid = lef + rig >> 1;
        if(lb <= mid) add_in(ls, lef, mid, lb, rb);
        if(rb > mid) add_in(rs, mid + 1, rig, lb, rb);
        pushup(now);
    }
    void add_out(const int &now, const int &lef, const int &rig, const int &lb, const int &rb, const int &tar)
    {
        add_in(root[now], 1, dot, lb, rb);
        if(lef == rig) return;
        int mid = lef + rig >> 1;
        if(tar <= mid) add_out(lss, lef, mid, lb, rb, tar);
        else add_out(rss, mid + 1, rig, lb, rb, tar);
    }
    ll query_in(const int &now, const int &lef, const int &rig, const int &lb, const int &rb)
    {
        if(!now) return 0;
        if(lef >= lb && rig <= rb) return tree[now].sum;
        pushdown(now, lef, rig);
        int mid = lef + rig >> 1;
        ll ret = 0;
        if(lb <= mid) ret += query_in(ls, lef, mid, lb, rb);
        if(rb > mid) ret += query_in(rs, mid + 1, rig, lb, rb);
        return ret;
    }
    int solve(const int &now, const int &lef, const int &rig, const int &lb, const int &rb, const ll &kth)
    {
        if(lef == rig) return buf[lef];
        int mid = lef + rig >> 1;
        ll rem = query_in(root[rss], 1, dot, lb, rb);
        if(rem < kth) return solve(lss, lef, mid, lb, rb, kth - rem);
        else return solve(rss, mid + 1, rig, lb, rb, kth);
    }
}
IN int getid(int now) {return std::lower_bound(buf + 1, buf + 1 + dif, now) - buf;}
int main(void)
{
    in(dot), in(q);
    for (R int i = 1; i <= q; ++i)
    {
        in(op[i].typ), in(op[i].lef), in(op[i].rig), in(op[i].kth);
        if(op[i].typ & 1) buf[++bcnt] = op[i].kth;
    }
    std::sort(buf + 1, buf + 1 + bcnt);
    dif = std::unique(buf + 1, buf + 1 + bcnt) - buf - 1;
    for (R int i = 1; i <= q; ++i)
    {
        if(op[i].typ & 1) PT::add_out(1, 1, dif, op[i].lef, op[i].rig, getid(op[i].kth));
        else printf("%d\n", PT::solve(1, 1, dif, op[i].lef, op[i].rig, op[i].kth));
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值