权值线段树

权值线段树

1、一种可以处理整个数组的第 \(k\) 大数或第 \(k\) 小数的算法,依名可知,该线段树是由数值来建线段树的,如果对于权值为 \(1e9\) 这样的数据,我们也可以通过离散化的方式进行值域压缩,从而缩小到 \(2e5\) 的样子,和普通线段树一样,可以通过懒标记来维护一段区间的修改,每次修改我们都可以添加任意元素。

2、时间复杂度:\(O(nlogn)\),空间复杂度:\(O(nlogn)\)

3、模版:洛谷P8747

4、和平衡树差不多的用法,主要可以用来求解查询第 \(k\) 大的数是谁查询第 \(k\) 小的数是谁查询 \(x\) 的排名\(x\) 的前驱(小于 \(x\) 的最大数)求x的后驱(大于 \(x\) 的最小数),平衡树传送门:平衡树

template<typename T>
struct W_tree{
    struct node{
        T siz, sum, lzy;
        node(T siz = 0, T sum = 0, T lzy = 0) : siz(siz), sum(sum), lzy(lzy) {}
        #define ls root << 1
        #define rs root << 1 | 1
		#define rt(x) tree[x]
    };
	
    int mx;
    vector<node> tree;
    
    W_tree() {}
	W_tree(int mx_) : tree(mx_ << 4) {
		mx = mx_;
		build(1, 1, mx_);
	}
	
	inline void init() {
	}

	inline node hb(node i, node j) {
		node k;
		k.siz = i.siz + j.siz;
		k.sum = i.sum + j.sum;
		return k;
	}

	inline void push_up(int root, int l, int r) {
		rt(root) = hb(rt(ls), rt(rs));
	}

	inline void push_down(int root, int l, int r) {
		int mid = l + r >> 1;
		if (rt(root).lzy) {
			rt(ls).sum += rt(root).lzy * rt(ls).siz;
			rt(rs).sum += rt(root).lzy * rt(rs).siz;
			rt(ls).lzy += rt(root).lzy;
			rt(rs).lzy += rt(root).lzy;
			rt(root).lzy = 0;
		}
	}

	inline void build(int root, int l, int r) {// 建树
		int mid = l + r >> 1;
		if (l == r) {
            rt(root) = node(1, 0, 0);
			return;
		}
		build(ls, l, mid);
		build(rs, mid + 1, r);
		push_up(root, l, r);
	}

	inline void ins(int root, int l, int r, int ql, int qr, const T &v) {// 插入或删除
		if (r < ql || l > qr) {
			return;
		}
		if (l >= ql && r <= qr) {
			rt(root).sum += v * rt(root).siz;
			rt(root).lzy += v;
			return;
		}
		push_down(root, l, r);
		int mid = l + r >> 1;
		ins(ls, l, mid, ql, qr, v);
		ins(rs, mid + 1, r, ql, qr, v);
		push_up(root, l, r);
	}
	
	inline int query_kth_max(int root, int l, int r, T k) {// 查询第k大的数是谁
		int mid = l + r >> 1;
		if (l == r) {
			return l;
		}
		push_down(root, l, r);
		T rg = rt(rs).sum;
		if (k <= rg) {
			return query_kth_max(rs, mid + 1, r, k);
		} else {
			return query_kth_max(ls, l, mid, k - rg);
		}
	}
	
	inline int query_kth_min(int root, int l, int r, T k) {// 查询第k小的数是谁
		int mid = l + r >> 1;
		if (l == r) {
			return l;
		}
		push_down(root, l, r);
		T lf = rt(ls).sum;
		if (k <= lf) {
			return query_kth_min(ls, l, mid, k);
		} else {
			return query_kth_min(rs, mid + 1, r, k - lf);
		}
	}

	inline T rtk(int root, int l, int r, int ql, int qr) {// 查询x的排名
		int mid = l + r >> 1;
		if (r < ql || l > qr) {
			return 0;
		}
		if (l >= ql && r <= qr) {
			return rt(root).sum;
		}
		push_down(root, l, r);
		T sum{};
		sum += rtk(ls, l, mid, ql, qr);
		sum += rtk(rs, mid + 1, r, ql, qr);
		return sum;
	}

	inline int pre(int x) {// 求x的前驱(小于x的最大数)
		T sum = rtk(1, 1, mx, 1, x);
		return query_kth_min(1, 1, mx, sum);
	}

	inline int nxt(int x) {// 求x的后驱(大于x的最小数)
		T sum = rtk(1, 1, mx, x, mx);
		return query_kth_max(1, 1, mx, sum);
	}
};

void solve() {
	int m;
	read(m);
	int cnt = 0;
	set<i64> s;
	map<int, int> mp, mp2;
	vector<PI> a(m);
	for (int i = 0; i < m; i++) {
		read(a[i].first, a[i].second);
		s.insert(a[i].second);
		s.insert(a[i].second - 1);
		s.insert(a[i].second + 1);
	}
	for (auto p : s) {
		mp[p] = ++cnt;
		mp2[cnt] = p;
	}
	W_tree<i64> tr(cnt);
	for (auto [op, x] : a) {
		if (op == 1) {
			tr.ins(1, 1, cnt, mp[x], mp[x], 1);
		} else if (op == 2) {
			tr.ins(1, 1, cnt, mp[x], mp[x], -1);
		} else if (op == 3) {
			i64 sum = tr.rtk(1, 1, cnt, 1, mp[x - 1]) + 1;
			printf("%lld\n", sum);
		} else if (op == 4) {
			int sum = tr.query_kth_min(1, 1, cnt, x);
			printf("%d\n", mp2[sum]);
		} else if (op == 5) {
			int sum = tr.pre(mp[x - 1]);
			printf("%d\n", mp2[sum]);
		} else if (op == 6) {
			int sum = tr.nxt(mp[x + 1]);
			printf("%d\n", mp2[sum]);
		}
	}
}
posted @ 2024-08-15 02:42  grape_king  阅读(28)  评论(0)    收藏  举报