BZOJ传送门
洛谷传送门
题目描述
有 N N 个位置,个操作。操作有两种,每次操作如果是 1 a b c 1 a b c 的形式表示在第 a a 个位置到第个位置,每个位置加入一个数 c c 如果是形式,表示询问从第 a a 个位置到第个位置,第 C C 大的数是多少。
输入输出格式
输入格式:
第一行, 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 的数只有 , 位置 2 2 的数也只有 。 第二个操作 后位置 1的数有 1 1 、 ,位置 2 2 的数也有 、 2 2 。 第三次询问 位置 到位置 1 1 第 大的数 是 1 1 。 第四次询问 位置 到位置 1 1 第 大的数是 2 2 。 第五次询问 位置 到位置 2 2 第 大的数是 1 1 。
a≤b≤N
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));
}
}