Dynamic Rankings(动态主席树 : 整体二分)

题目链接: Dynamic Rankings

大致题意

有眼就行

解题思路

动态主席树整体二分 的模板题!

关于主席树的解法, 因为有单点修改, 所以我们要采用动态主席树. 具体方法就是, 把主席树中的存储方式采用树状数组的存储方式.

因此查询的时候, 原本是传入一个l - 1版本和一个当前版本r, 但是由于采用了树状数组的存储方式, 就要传入logn个版本. 修改同理.

整体二分

不得不说, 这题整体二分跑的是真的快!

AC代码

主席树
//例题: 求动态区间第K小数 (静态树 + 动态树做法)
//思路: 用树状数组的存储方式维护前缀和. 静态树记录初始情况, 动态树维护修改情况
//时间复杂度: O((n + m)lognlogn) 空间复杂度: O(nlogn + mlognlogn)
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10, M = N * 17 * 17; // M为节点数
int n, m;
int w[N];
vector<int> v(1, -0x3f3f3f3f); int len = 0; //len为离散化后值域边界, 但不是树的边界.
int find(int x) { return lower_bound(v.begin(), v.end(), x) - v.begin(); }


struct operation { //操作离线
	int tp, l, r, k;
	//      x  y  N
}; vector<operation> area;


struct node {
	int l, r;
	int cou;
}t[M];
int rt[N], root[N], ind; //root是动态树, rt是静态树
int build(int a, int tl, int tr, int p) {
	int x = ++ind; t[x] = t[p];
	t[x].cou++;
	if (tl == tr) return x;
	int mid = tl + tr >> 1;
	if (a <= mid) t[x].l = build(a, tl, mid, t[p].l);
	else t[x].r = build(a, mid + 1, tr, t[p].r);
	return x;
}

void modify(int a, int c, int tl, int tr, int& x) { //修改动态树
	if (!x) x = ++ind;
	t[x].cou += c;
	if (tl == tr) return;

	int mid = tl + tr >> 1;
	a <= mid ? modify(a, c, tl, mid, t[x].l) : modify(a, c, mid + 1, tr, t[x].r);
}

int query(int k, int tl, int tr, vector<int>& p, vector<int>& x) { //查询树中第k小
	if (tl == tr) return tl; //查询类似单树查询, 只不过p和x都变成了数组(logn棵树)
	int cou = 0;
	for (auto& op : x) cou += t[t[op].l].cou;
	for (auto& op : p) cou -= t[t[op].l].cou;

	int mid = tl + tr >> 1;
	vector<int> np, nx;
	if (cou >= k) {
		for (auto& op : p) np.push_back(t[op].l);
		for (auto& op : x) nx.push_back(t[op].l);
		return query(k, tl, mid, np, nx);
	}

	for (auto& op : p) np.push_back(t[op].r);
	for (auto& op : x) nx.push_back(t[op].r);
	return query(k - cou, mid + 1, tr, np, nx); //记得减去左边贡献
}



int lowbit(int x) { return x & -x; }
void add(int x, int c) {
	for (int i = x; i <= n; i += lowbit(i)) modify(w[x], c, 1, len, root[i]);
}
int ask(int l, int r, int k) {
	vector<int> np, nx;
	nx.push_back(rt[r]), np.push_back(rt[l - 1]); //推入静态树

	for (int i = r; i; i -= lowbit(i)) nx.push_back(root[i]);
	for (int i = l - 1; i; i -= lowbit(i)) np.push_back(root[i]); //注意l - 1
	return query(k, 1, len, np, nx);
}

int main()
{
	cin >> n >> m;
	rep(i, n) scanf("%d", &w[i]), v.push_back(w[i]);
	rep(i, m) {
		char s[2]; scanf("%s", s);
		if (*s == 'Q') {
			int l, r, k; scanf("%d %d %d", &l, &r, &k);
			area.push_back({ 1, l, r, k });
		}
		else {
			int a, c; scanf("%d %d", &a, &c);
			area.push_back({ 2, a, c, NULL }); //注意存下标, 树状数组要根据位置修改
			v.push_back(c);
		}
	}
	sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end());
	len = v.size() - 1;

	rep(i, n) { //建静态树
		rt[i] = build(w[i] = find(w[i]), 1, len, rt[i - 1]);
		//add(i, 1); 如果不用build, 都用add, 则为纯动态树的方法.(费空间)
	}

	for (auto& op : area) {
		if (op.tp == 1) printf("%d\n", v[ask(op.l, op.r, op.k)]);
		else {
			add(op.l, -1);
			w[op.l] = find(op.r); //记得修改w数组
			add(op.l, 1);
		}
	}
	return 0;
}
整体二分
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
#define debug(a) cout << #a << " = " << a << endl;
using namespace std;
typedef long long ll;
const int N = 1E5 + 10;
int n, m;
int w[N], res[N];
struct operation {
	int tp, l, r, k, id;
	//      a, c, f, NULL
}; vector<operation> area;

int t[N];
int lowbit(int x) { return x & -x; }
void add(int x, int c) { for (int i = x; i <= n; i += lowbit(i)) t[i] += c; }
int ask(int x) {
	int res = 0;
	for (int i = x; i; i -= lowbit(i)) res+= t[i];
	return res;
}
int ask(int l, int r) { return ask(r) - ask(l - 1); }


void fact(int l, int r, vector<operation>& q) {
	if (q.empty()) return;
	if (l == r) {
		for (auto& op : q) if (!op.tp) res[op.id] = l;
		return;
	}

	int mid = l + r >> 1;
	vector<operation> ql, qr;
	for (auto& op : q) {
		if (op.tp) {
			if (op.r <= mid) add(op.l, op.k), ql.push_back(op);
			else qr.push_back(op);
		}
		else {
			int cou = ask(op.l, op.r);
			if (cou >= op.k) ql.push_back(op);
			else op.k -= cou, qr.push_back(op);
		}
	}

	for (auto& op : ql) if (op.tp) add(op.l, -op.k);

	fact(l, mid, ql), fact(mid + 1, r, qr);
}
int main()
{
	cin >> n >> m;
	rep(i, n) {
		scanf("%d", &w[i]);
		area.push_back({ 1, i, w[i], 1, NULL });
	}

	rep(i, m) {
		res[i] = -1;
		char s[2]; scanf("%s", s);
		if (*s == 'Q') {
			int l, r, k; scanf("%d %d %d", &l, &r, &k);
			area.push_back({ 0, l, r, k, i });
		}
		else {
			int a, c; scanf("%d %d", &a, &c);
			area.push_back({ 1, a, w[a], -1, NULL });
			w[a] = c;
			area.push_back({ 1, a, w[a], 1, NULL });
		}
	}

	fact(0, 0x3f3f3f3f, area);
	rep(i, m) if (res[i] != -1) printf("%d\n", res[i]);
	return 0;
}

END

上一篇:Codeforces Round #734 (Div. 3) (A-C)


下一篇:21.12.8抛硬币问题