【LOJ3273】「JOISC 2020 Day1」扫除

题目链接

点击打开链接

题目解法

考虑子任务 3 3 的解法。
可以发现,将所有元素按照 x x 升序为第一关键字, y y 降序为第二关键字排序,任何操作不会改变元素的相对顺序。由此,用线段树维护元素序列,修改时在线段树上二分出受到影响的区间,可以将修改操作看做一次区间对某一维坐标赋值的操作。
时间复杂度 O ( M + Q L o g M ) O(M+QLogM)

考虑子任务 4 4 ,即不存在插入操作的解法。
此时,一旦一个元素被一次修改操作影响到,它和其余被影响到过的元素之间的相对顺序便不会再发生变化。由此,可以考虑用一棵动态开点的线段树维护尚未被影响到过的元素,在修改时找到被此次修改第一次影响到的元素,再用一棵平衡树维护已经被影响到过的元素即可。
时间复杂度 O ( M + Q L o g M ) O(M+QLogM)

回到原题,我们只需要将操作离线,采用时间分治或线段树分治的方式规避插入操作即可。
以下代码采用了线段树分治的方式,由于题目时限并不宽松,进行了一定的常数优化。

时间复杂度 O ( ( M + Q ) L o g Q L o g N ) O((M+Q)LogQLogN)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e6 + 5;
const int MAXP = 5e7 + 5;
const int R    = 1e9;
typedef long long ll;
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;
}
struct Splay {
	struct Node {
		int child[2], father;
		int x, y, tagx, tagy;
	} a[MAXN];
	int root;
	void clear(int root) {
		if (a[root].child[0]) clear(a[root].child[0]);
		if (a[root].child[1]) clear(a[root].child[1]);
		a[root].child[0] = a[root].child[1] = a[root].father = 0;
		a[root].x = a[root].y = a[root].tagx = a[root].tagy = 0;
	}
	void init(int n, int m) {
		clear(root);
		root = m + 1;
		a[m + 1].x = -1, a[m + 1].y = n + 1;
		a[m + 2].x = n + 1, a[m + 2].y = -1;
		a[m + 1].child[1] = m + 2;
		a[m + 2].father = m + 1;
	}
	void pushdown(int root) {
		if (a[root].tagx) {
			if (a[root].child[0]) {
				a[a[root].child[0]].x = a[root].tagx;
				a[a[root].child[0]].tagx = a[root].tagx;
			}
			if (a[root].child[1]) {
				a[a[root].child[1]].x = a[root].tagx;
				a[a[root].child[1]].tagx = a[root].tagx;
			}
			a[root].tagx = 0;
		}
		if (a[root].tagy) {
			if (a[root].child[0]) {
				a[a[root].child[0]].y = a[root].tagy;
				a[a[root].child[0]].tagy = a[root].tagy;
			}
			if (a[root].child[1]) {
				a[a[root].child[1]].y = a[root].tagy;
				a[a[root].child[1]].tagy = a[root].tagy;
			}
			a[root].tagy = 0;
		}
	}
	bool get(int root) {return a[a[root].father].child[1] == root; }
	void rotate(int x) {
		int f = a[x].father, g = a[f].father;
		pushdown(f); pushdown(x);
		bool tmp = get(x), tnp = get(f);
		a[a[x].child[tmp ^ 1]].father = f;
		a[f].child[tmp] = a[x].child[tmp ^ 1];
		a[x].child[tmp ^ 1] = f;
		a[f].father = x;
		a[x].father = g;
		if (g != 0) a[g].child[tnp] = x;
	}
	void splay(int x) {
		pushdown(x);
		for (int f = a[x].father; (f = a[x].father) != 0; rotate(x)) {
			pushdown(f); pushdown(x);
			if (a[f].father != 0) {
				if (get(f) == get(x)) rotate(f);
				else rotate(x);
			}
		}
		root = x;
	}
	void spaly(int x) {
		pushdown(x);
		for (int f = a[x].father; (f = a[x].father) != root; rotate(x)) {
			pushdown(f); pushdown(x);
			if (a[f].father != root) {
				if (get(f) == get(x)) rotate(f);
				else rotate(x);
			}
		}
	}
	void insert(int pos, int x, int y) {
		a[pos].x = x, a[pos].y = y;
		int now = root;
		while (true) {
			pushdown(now);
			bool tmp = x > a[now].x || (x == a[now].x && y < a[now].y);
			if (a[now].child[tmp] == 0) {
				a[now].child[tmp] = pos;
				a[pos].father = now;
				splay(pos);
				return;
			}
			now = a[now].child[tmp];
		}
	}
	pair <int, int> query(int pos) {
		splay(pos);
		return make_pair(a[pos].x, a[pos].y);
	}
	void findleft(int y) {
		int now = root, last = root, res = 0;
		while (now != 0) {
			pushdown(now), last = now;
			if (a[now].y > y) res = now, now = a[now].child[1];
			else now = a[now].child[0];
		}
		assert(res != 0), splay(res);
	}
	void findright(int x) {
		int now = root, last = root, res = 0;
		while (now != 0) {
			pushdown(now), last = now;
			if (a[now].x > x) res = now, now = a[now].child[0];
			else now = a[now].child[1];
		}
		assert(res != 0), spaly(res);
	}
	void puttag(int x, int y, int tagx, int tagy) {
		findleft(y);
		findright(x);
		int pos = a[a[root].child[1]].child[0];
		if (pos != 0) {
			if (tagx != 0) a[pos].x = tagx, a[pos].tagx = tagx;
			if (tagy != 0) a[pos].y = tagy, a[pos].tagy = tagy;
		}
	}
} Splay;
vector <int> affected;
int qrys; pair <int, int> ans[MAXN];
pair <pair <int, int>, int> s[MAXN];
int timer, last[MAXN], type[MAXN], val[MAXN];
int n, m, q, x[MAXN], y[MAXN]; bool vis[MAXN];
struct SegmentTree {
	struct Node {
		int lc, rc;
		int Min;
	} a[MAXP];
	int root, size;
	map <int, vector <pair <int, int>>> mp;
	void init() {
		root = size = 0;
		mp.clear();
	}
	void update(int root) {
		a[root].Min = R + 1;
		if (a[root].lc) chkmin(a[root].Min, a[a[root].lc].Min);
		if (a[root].rc) chkmin(a[root].Min, a[a[root].rc].Min);
	}
	int newnode() {
		size++;
		a[size].lc = a[size].rc = 0;
		a[size].Min = R + 1;
		return size;
	}
	void modify(int &root, int l, int r, int pos, int val) {
		if (root == 0) root = newnode();
		int now = root;
		while (true) {
			chkmin(a[now].Min, val);
			if (l == r) return;
			int mid = (l + r) / 2;
			if (mid >= pos) {
				if (a[now].lc == 0) a[now].lc = newnode();
				now = a[now].lc, r = mid;
			} else {
				if (a[now].rc == 0) a[now].rc = newnode();
				now = a[now].rc, l = mid + 1;
			}
		}
	}
	void insert(int x, int y, int home) {
		mp[x].emplace_back(y, home);
		modify(root, 0, R, x, y);
	}
	void work(int x, int y) {
		static int rt[MAXN], vl[MAXN], vr[MAXN];
		static bool lf[MAXN];
		int ql = 0, qr = 0;
		rt[0] = root, vl[0] = 0, vr[0] = R;
		if (root == 0) return;
		while (ql <= qr) {
			int root = rt[ql], l = vl[ql], r = vr[ql];
			lf[ql] = l == r, ql++;
			if (a[root].Min > y) continue;
			if (x >= r) {
				if (l == r) {
					while (!mp[l].empty() && mp[l].back().first <= y) {
						affected.push_back(mp[l].back().second);
						mp[l].pop_back();
					}
					if (mp[l].empty()) a[root].Min = R + 1;
					else a[root].Min = mp[l].back().first;
					continue;
				}
				int mid = (l + r) / 2;
				if (a[root].lc) {
					qr++;
					rt[qr] = a[root].lc;
					vl[qr] = l, vr[qr] = mid;
				}
				if (a[root].rc) {
					qr++;
					rt[qr] = a[root].rc;
					vl[qr] = mid + 1, vr[qr] = r;
				}
			} else {
				int mid = (l + r) / 2;
				if (a[root].lc) {
					qr++;
					rt[qr] = a[root].lc;
					vl[qr] = l, vr[qr] = mid;
				}
				if (x > mid && a[root].rc) {
					qr++;
					rt[qr] = a[root].rc;
					vl[qr] = mid + 1, vr[qr] = r;
				}
			}
		}
		for (int i = qr; i >= 0; i--)
			if (!lf[i]) update(rt[i]);
	}
	void initII() {
		for (auto &x : mp) {
			sort(x.second.begin(), x.second.end());
			reverse(x.second.begin(), x.second.end());
		}
	}
} X, Y;
struct DivideSegmentTree {
	struct Node {
		int lc, rc;
		vector <pair <int, int>> q;
	} a[MAXN * 2];
	int root, size, rng;
	void build(int &root, int l, int r) {
		root = ++size;
		if (l == r) return;
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
	}
	void build(int x) {
		rng = x;
		build(root, 1, rng);
	}
	void putquery(int root, int l, int r, int ql, int qr, pair <int, int> x) {
		if (l == ql && r == qr) {
			a[root].q.push_back(x);
			return;
		}
		int mid = (l + r) / 2;
		if (ql <= mid) putquery(a[root].lc, l, mid, ql, min(qr, mid), x);
		if (mid + 1 <= qr) putquery(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, x);
	}
	void putquery(int l, int r, int x, int home) {
		putquery(root, 1, rng, l, r, make_pair(x, home));
	}
	void solve(int root, int l, int r) {
		int cnt = 0;
		for (int i = l; i <= r; i++)
			cnt += type[i] != 0;
		if (cnt > 30 && a[root].q.size() > 30) {
			X.init(), Y.init(), Splay.init(n, m);
			for (auto p : a[root].q) {
				int pos = p.first;
				vis[pos] = false;
				X.insert(x[pos], y[pos], pos);
				Y.insert(y[pos], x[pos], pos);
			}
			X.initII(), Y.initII();
			for (int i = l; i <= r; i++) {
				if (type[i] == 2) {
					Splay.puttag(n - val[i], val[i], n - val[i], 0);
					affected.clear(), Y.work(val[i], n - val[i]);
					for (auto p : affected)
						if (!vis[p]) {
							vis[p] = true;
							x[p] = n - val[i];
							Splay.insert(p, x[p], y[p]);
						}
				}
				if (type[i] == 3) {
					Splay.puttag(val[i], n - val[i], 0, n - val[i]);
					affected.clear(), X.work(val[i], n - val[i]);
					for (auto p : affected)
						if (!vis[p]) {
							vis[p] = true;
							y[p] = n - val[i];
							Splay.insert(p, x[p], y[p]);
						}
				}
			}
			for (auto p : a[root].q) {
				int pos = p.first;
				if (vis[pos]) {
					pair <int, int> res = Splay.query(pos);
					x[pos] = res.first, y[pos] = res.second;
				}
				ans[p.second] = make_pair(x[pos], y[pos]);
			}
		} else {
			vector <int> poss;
			for (int i = l; i <= r; i++)
				if (type[i] != 0) poss.push_back(i);
			for (auto p : a[root].q) {
				int pos = p.first;
				for (auto q : poss)
					if (type[q] == 2) {
						if (y[pos] <= val[q]) chkmax(x[pos], n - val[q]);
					} else {
						if (x[pos] <= val[q]) chkmax(y[pos], n - val[q]);
					}
				ans[p.second] = make_pair(x[pos], y[pos]);
			}
		}
	}
	void work(int root, int l, int r) {
		solve(root, l, r);
		if (l != r) {
			int mid = (l + r) / 2;
			work(a[root].lc, l, mid);
			work(a[root].rc, mid + 1, r);
		}
	}
	void work() {
		work(root, 1, rng);
	}
} ST;
int main() {
	read(n), read(m), read(q);
	for (int i = 1; i <= m; i++)
		read(x[i]), read(y[i]);
	for (int i = 1; i <= q; i++) {
		int opt; read(opt);
		if (opt == 1) {
			int x; read(x); qrys++;
			s[qrys] = make_pair(make_pair(last[x] + 1, i), x);
			last[x] = i;
		}
		if (opt == 2 || opt == 3) {
			type[i] = opt;
			read(val[i]);
		}
		if (opt == 4) {
			m++; read(x[m]), read(y[m]);
			last[m] = i;
		}
	}
	ST.build(q);
	for (int i = 1; i <= qrys; i++)
		ST.putquery(s[i].first.first, s[i].first.second, s[i].second, i);
	ST.work();
	for (int i = 1; i <= qrys; i++)
		printf("%d %d\n", ans[i].first, ans[i].second);
	return 0;
}
发布了813 篇原创文章 · 获赞 93 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/105074209