【CodeChef】Count on a Treap

【题目链接】

【思路要点】

  • 若将一个序列按照元素大小排序,那么其对应的 T r e a p Treap Treap 即为权值对应的笛卡尔树,并且,被删除的元素可以视作权值为 0 0 0 的元素。
  • 一个点在 T r e a p Treap Treap 上所有的祖先即其左侧/右侧对应的所有权值为后/前缀最大值的点。
  • 离线操作,对权值离散化,并用线段树维护。
  • 插入删除操作可以通过线段树的单调修改实现。
  • 对于一个询问 ( x , y ) (x,y) (x,y) ,我们考虑计算 d e p t h x + d e p t h y − 2 ∗ d e p t h l c a ( x , y ) depth_x+depth_y-2*depth_{lca(x,y)} depthx+depthy2depthlca(x,y)
  • 其中 l c a ( x , y ) lca(x,y) lca(x,y) 即为区间 [ x , y ] [x,y] [x,y] 权值最大值的位置。 d e p t h x depth_x depthx 需要计算的是从 x x x 向左/右,权值作为后/前缀最大值出现的位置的个数,可用李超线段树维护。
  • 时间复杂度 O ( N L o g 2 N ) O(NLog^2N) O(NLog2N)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct SegmentTree {
	struct Node {
		int lc, rc, pre, suf;
		unsigned Max;
	} a[MAXN * 2];
	int root, size, n;
	void build(int &root, int l, int r) {
		root = ++size;
		a[root].Max = 0;
		a[root].pre = 0;
		a[root].suf = 0;
		if (l == r) return;
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
	}
	void init(int x) {
		n = x;
		root = size = 0;
		build(root, 1, n);
	}
	int calpre(int pos, unsigned d) {
		if (a[pos].lc == 0) {
			if (d < a[pos].Max) return 1;
			else return 0;
		}
		if (d < a[a[pos].rc].Max) return a[pos].pre + calpre(a[pos].rc, d);
		else return calpre(a[pos].lc, d);
	}
	int calsuf(int pos, unsigned d) {
		if (a[pos].lc == 0) {
			if (d < a[pos].Max) return 1;
			else return 0;
		}
		if (d < a[a[pos].lc].Max) return a[pos].suf + calsuf(a[pos].lc, d);
		else return calsuf(a[pos].rc, d);
	}
	void update(int root) {
		a[root].Max = max(a[a[root].lc].Max, a[a[root].rc].Max);
		a[root].pre = calpre(a[root].lc, a[a[root].rc].Max);
		a[root].suf = calsuf(a[root].rc, a[a[root].lc].Max);
	}
	void modify(int root, int l, int r, int pos, unsigned d) {
		if (l == r) {
			a[root].Max = d;
			return;
		}
		int mid = (l + r) / 2;
		if (mid >= pos) modify(a[root].lc, l, mid, pos, d);
		else modify(a[root].rc, mid + 1, r, pos, d);
		update(root);
	}
	void modify(int pos, unsigned d) {
		modify(root, 1, n, pos, d);
	}
	unsigned tmp;
	int querypre(int root, int l, int r, int pos) {
		if (pos >= r) {
			int ans = calpre(root, tmp);
			chkmax(tmp, a[root].Max);
			return ans;
		}
		int mid = (l + r) / 2, ans = 0;
		if (mid < pos) ans += querypre(a[root].rc, mid + 1, r, pos);
		ans += querypre(a[root].lc, l, mid, pos);
		return ans;
	}
	int querysuf(int root, int l, int r, int pos) {
		if (pos <= l) {
			int ans = calsuf(root, tmp);
			chkmax(tmp, a[root].Max);
			return ans;
		}
		int mid = (l + r) / 2, ans = 0;
		if (mid >= pos) ans += querysuf(a[root].lc, l, mid, pos);
		ans += querysuf(a[root].rc, mid + 1, r, pos);
		return ans;
	}
	int query(int x) {
		int ans = -1;
		tmp = 0; ans += querypre(root, 1, n, x);
		tmp = 0; ans += querysuf(root, 1, n, x);
		return ans;
	}
	int home;
	void lca(int root, int l, int r, int ql, int qr) {
		int mid = (l + r) / 2;
		if (l == ql && r == qr) {
			if (tmp > a[root].Max) return;
			chkmax(tmp, a[root].Max);
			if (l == r) {
				home = l;
				return;
			}
			lca(a[root].lc, l, mid, l, mid);
			lca(a[root].rc, mid + 1, r, mid + 1, r);
			return;
		}
		if (mid >= ql) lca(a[root].lc, l, mid, ql, min(qr, mid));
		if (mid + 1 <= qr) lca(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
	}
	int lca(int x, int y) {
		tmp = 0;
		if (x > y) swap(x, y);
		lca(root, 1, n, x, y);
		return home;
	}
	int query(int x, int y) {
		return query(x) + query(y) - 2 * query(lca(x, y));
	}
} ST;
set <unsigned> st; map <unsigned, int> mp;
int n, tot; unsigned opt[MAXN], x[MAXN], y[MAXN];
int main() {
	read(n);
	for (int i = 1; i <= n; i++) {
		read(opt[i]), read(x[i]);
		if (opt[i] != 1) read(y[i]);
		if (opt[i] == 0) st.insert(x[i]);
	}
	for (auto x : st) mp[x] = ++tot;
	ST.init(tot);
	for (int i = 1; i <= n; i++) {
		if (opt[i] == 0) {
			x[i] = mp[x[i]];
			ST.modify(x[i], y[i]);
		} else if (opt[i] == 1) {
			x[i] = mp[x[i]];
			ST.modify(x[i], 0);
		} else {
			x[i] = mp[x[i]];
			y[i] = mp[y[i]];
			writeln(ST.query(x[i], y[i]));
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值