带修主席树模板

洛谷模板题已过【P2617 Dynamic Rankings】

/* 动态持久化线段树(树状数组套线段树)模板类
 * 用于解决带修改的区间第k小和区间排名查询问题
 * 核心功能:
 *   - 单点更新 O(log n log V)
 *   - 区间第k小查询 O(log n log V)
 *   - 区间排名查询 O(log n log V)
 */
template<class Node>
struct DynamicPersistentSegmentTree {
	int n;                 // 原数组大小(树状数组大小)
	int V;                 // 离散化后的值域范围 [1, V]
	int tot = 0;           // 线段树节点计数器
	vector<Node> tr;       // 线段树节点池
	vector<int> roots;     // 树状数组的根节点数组(下标1~n)
	vector<int> arrL,arrR; // 临时存储树状数组路径节点

	DynamicPersistentSegmentTree(int n_, int V_) : n(n_), V(V_) {
		tr.reserve(n * __lg(n) * __lg(V) * 4);
		tr.resize(1);
		tr[0] = Node();
		roots.assign(n + 1, 0);
	}
	/* 单点更新操作
	 * @param pos: 原数组位置 (1-indexed)
	 * @param val: 离散化后的权值
	 * @param w: 更新值
	 * 时间复杂度:O(log n * log V)
	 */
	void update(int pos, int val, int w) {
		for (int i = pos; i <= n; i += i & -i) {
			roots[i] = insert(roots[i], 1, V, val, w);
		}
	}
	/* 区间第k小查询
	 * @param l: 区间左端点 (1-indexed)
	 * @param r: 区间右端点 (1-indexed)
	 * @param k: 查询的排名
	 * @return: 离散化后的权值
	 * 时间复杂度:O(log n * log V)
	 */
	int query_kth(int l, int r, int k) {
		get_roots(l - 1, arrL);
		get_roots(r, arrR);
		return query_kth(arrL, arrR, k, 1, V);
	}
	/* 区间排名查询(小于x的数的个数)
	 * @param l: 区间左端点
	 * @param r: 区间右端点
	 * @param x: 离散化后的权值
	 * @return: 排名值
	 * 时间复杂度:O(log n * log V)
	 */
	int query_rank(int l, int r, int x) {
		get_roots(l - 1, arrL);
		get_roots(r, arrR);
		return query_rank(arrL, arrR, 1, V, x);
	}

  private:
	int insert(int now, int l, int r, int pos, int w) {
		if (!now) {
			tr.push_back(Node());
			now = ++tot;
		}
		tr[now].v += w;
		if (l == r) {
			return now;
		}
		int mid = (l + r) >> 1;
		if (pos <= mid) {
			tr[now].l = insert(tr[now].l, l, mid, pos, w);
		}
		else {
			tr[now].r = insert(tr[now].r, mid + 1, r, pos, w);
		}
		return now;
	}
	void get_roots(int x, vector<int>& arr) {
		arr.clear();
		while (x) {
			arr.push_back(roots[x]);
			x -= x & -x;
		}
	}
	int query_kth(vector<int>& arrL, vector<int>& arrR, int k, int l, int r) {
		if (l == r) {
			return l;
		}
		int mid = (l + r) >> 1;
		int sum = 0;
		// 计算左子树的总和(右区间和 - 左区间和)
		for (int i : arrR) {
			sum += tr[tr[i].l].v;  // 累加右区间左子树
		}
		for (int i : arrL) {
			sum -= tr[tr[i].l].v;  // 减去左区间左子树
		}
		if (k <= sum) {
			for (int& i : arrL) {
				i = tr[i].l;
			}
			for (int& i : arrR) {
				i = tr[i].l;
			}
			return query_kth(arrL, arrR, k, l, mid);
		}
		else {
			for (int& i : arrL) {
				i = tr[i].r;
			}
			for (int& i : arrR) {
				i = tr[i].r;
			}
			return query_kth(arrL, arrR, k - sum, mid + 1, r);
		}
	}
	int query_rank(vector<int>& arrL, vector<int>& arrR, int l, int r, int k) {
		if (l == r) {
			return 0;
		}
		int mid = (l + r) >> 1;
		if (k <= mid) {
			for (int& i : arrL) {
				i = tr[i].l;
			}
			for (int& i : arrR) {
				i = tr[i].l;
			}
			return query_rank(arrL, arrR, l, mid, k);
		}
		else {
			// 目标值在右子树:先计算左子树总和
			int sum = 0;
			for (int &i : arrR) {
				sum += tr[tr[i].l].v;    // 右区间左子树和
				i = tr[i].r;
			}
			for (int &i : arrL) {
				sum -= tr[tr[i].l].v;    // 减去左区间左子树和
				i = tr[i].r;
			}
			return sum + query_rank(arrL, arrR, mid + 1, r, k);
		}
	}
};

struct Node {
	int l, r, v;
	Node(): l(0), r(0), v(0) {}
};
posted @ 2025-08-12 20:39  Ke_scholar  阅读(20)  评论(1)    收藏  举报