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;
}
上一篇:BZOJ 3110: [Zjoi2013]K大数查询 树套树 整体二分


下一篇:BZOJ 3112: [Zjoi2013]防守战线 ZKW费用流