K大数查询[ZJOI2013]
Posted ak-dream
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 imes 10^4), (1le l,r le n), (1) 操作中 (|c|le n), (2) 操作中 (1le c < 2^{63})
题解
暴力树套树
外层维护权值线段树 内层是区间修改线段树
每次修改就把包含(c)的那些权值线段树节点上的 每个内层线段树的([l,r])区间(+1)
查询就在权值线段树上二分。。。
虽然要区间修改 但是空间其实是不会爆的 一次修改其实最多会新建(log^2 n)个内层线段树节点(并不会证明)
时间复杂度似乎是(O(mlog^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
", ans - n);
}
}
return 0;
}
以上是关于K大数查询[ZJOI2013]的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ3110 [ZJOI2013] K大数查询(加强数据)
bzoj 3110 [Zjoi2013]K大数查询 整体二分