题目链接
题目解法
考虑子任务
的解法。
可以发现,将所有元素按照
升序为第一关键字,
降序为第二关键字排序,任何操作不会改变元素的相对顺序。由此,用线段树维护元素序列,修改时在线段树上二分出受到影响的区间,可以将修改操作看做一次区间对某一维坐标赋值的操作。
时间复杂度
。
考虑子任务
,即不存在插入操作的解法。
此时,一旦一个元素被一次修改操作影响到,它和其余被影响到过的元素之间的相对顺序便不会再发生变化。由此,可以考虑用一棵动态开点的线段树维护尚未被影响到过的元素,在修改时找到被此次修改第一次影响到的元素,再用一棵平衡树维护已经被影响到过的元素即可。
时间复杂度
。
回到原题,我们只需要将操作离线,采用时间分治或线段树分治的方式规避插入操作即可。
以下代码采用了线段树分治的方式,由于题目时限并不宽松,进行了一定的常数优化。
时间复杂度 。
#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;
}