K大数查询[ZJOI2013]
【题目描述】
你需要维护 \(n\) 个可重整数集,集合的编号从 \(1\) 到 \(n\)。
这些集合初始都是空集,有 \(m\) 个操作:
1 l r c
:表示将 \(c\) 加入到编号在 \([l,r]\) 内的集合中2 l r c
:表示表示查询编号在 \([l,r]\) 内的集合的并集(可重)中,第 \(c\) 大的数是多少。
【输入/输出格式】
不关心
\(1 \le n,m \le 5\times 10^4\), \(1\le l,r \le n\), \(1\) 操作中 \(|c|\le n\), \(2\) 操作中 \(1\le c < 2^{63}\)
题解
暴力树套树
外层维护权值线段树 内层是区间修改线段树
每次修改就把包含\(c\)的那些权值线段树节点上的 每个内层线段树的\([l,r]\)区间\(+1\)
查询就在权值线段树上二分。。。
虽然要区间修改 但是空间其实是不会爆的 一次修改其实最多会新建\(\log^2 n\)个内层线段树节点(并不会证明)
时间复杂度似乎是\(O(m\log^2 n)\)
p.s. \(c\)可能是负数 注意要加一个偏移量
代码
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, f = 1; char ch = getchar();
for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ '0');
return x * f;
}
int n, m;
//内层线段树
struct tree{
int lc, rc, sum, tag;
} tr[15000005];
int tot;
inline void add(int ind, int l, int r, int v) {
tr[ind].sum += (r - l + 1) * v;
tr[ind].tag += v;
}
inline void pushdown(int ind, int l, int r) {
if (!tr[ind].tag) return;
int mid = (l + r) >> 1;
if (!tr[ind].lc) tr[ind].lc = ++tot;
add(tr[ind].lc, l, mid, tr[ind].tag);
if (!tr[ind].rc) tr[ind].rc = ++tot;
add(tr[ind].rc, mid + 1, r, tr[ind].tag);
tr[ind].tag = 0;
}
void update(int &ind, int l, int r, int x, int y) {
if (!ind) ind = ++tot;
if (x <= l && r <= y) {
add(ind, l, r, 1);
return;
}
pushdown(ind, l, r);
int mid = (l + r) >> 1;
if (x <= mid) update(tr[ind].lc, l, mid, x, y);
if (mid < y) update(tr[ind].rc, mid+1, r, x, y);
tr[ind].sum = (tr[tr[ind].lc].sum + tr[tr[ind].rc].sum);
}
int query(int ind, int l, int r, int x, int y) {
if (!ind) return 0;
if (x <= l && r <= y) return tr[ind].sum;
pushdown(ind, l, r);
int mid = (l + r) >> 1, ret = 0;
if (x <= mid) ret += query(tr[ind].lc, l, mid, x, y);
if (mid < y) ret += query(tr[ind].rc, mid+1, r, x, y);
return ret;
}
//外层线段树
struct Tree{
int lc, rc;
} Tr[200005];
int Rt, Tot, rt[200005];
void Update(int &ind, int l, int r, int x, int y, int pos) {
if (!ind) ind = ++Tot;
update(rt[ind], 1, n, x, y);
if (l == r) return;
int mid = (l + r) >> 1;
if (pos <= mid) Update(Tr[ind].lc, l, mid, x, y, pos);
else Update(Tr[ind].rc, mid+1, r, x, y, pos);
}
int Query(int ind, int l, int r, int x, int y, int k) {
if (l == r) return l;
int mid = (l + r) >> 1;
int cnt = query(rt[Tr[ind].rc], 1, n, x, y);
if (k <= cnt) return Query(Tr[ind].rc, mid+1, r, x, y, k);
else return Query(Tr[ind].lc, l, mid, x, y, k - cnt);
}
int main() {
n = read(), m = read();
for (int i = 1, tp, a, b, c; i <= m; i++) {
tp = read(), a = read(), b = read(), c = read();
if (tp == 1) {
Update(Rt, 0, n<<1, a, b, c + n);
} else {
int ans = Query(Rt, 0, n<<1, a, b, c);
printf("%d\n", ans - n);
}
}
return 0;
}