感觉很妙啊,应该不止蓝吧?
首先一个转化是每次建桥操作就相当于交换两条链的后半部分,可以看看扶苏那篇题解的图。
我们将每个点表示为形如 \((x, y)\) 的二元组表示它初始在第 \(x\) 行第 \(y\) 列,按 \(y\) 为键值排序,那么一次询问就是查询一条链的最大值。
这些操作可以用 FHQ Treap 完成,就是每次修改 \((a, b)\) 就把两棵 Treap 分别按 \(\le b\) split 成四棵,交换后再 merge。查询直接暴力跳右儿子就行。
但是显然不能存 \(nm\) 个点。考虑我维护连续段而不是维护一个个单点,每个连续段形如 \((x, l, r)\) 表示初始在第 \(x\) 行第 \(l\) 列到第 \(r\) 列的点,意义是它们在同一条链上并且连边 \(l \to l + 1 \to \cdots \to r\)。
那么每次修改操作会多产生 \(4\) 个段,所以段的个数就是 \(O(n + q)\) 的,可以接受。
实现时比较繁琐,每一行要额外开个 set 维护原来在这一行的段方便对于每次修改找到 \(b\) 所在的段,还要开个 map 维护每个三元组 \((x, l, r)\) 对应到 Treap 上的哪个点。为了找对应结点的根还要维护每个点的父亲。
总时间复杂度是 \(O((n + q) \log n)\)。
code
// Problem: P9358 [ICPC2022 Xi'an R] Bridge
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P9358
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;
const int maxn = 500100;
int n, m, q;
set<pii> S[maxn];
int ls[maxn], rs[maxn], p[maxn], sz[maxn], rt[maxn], fa[maxn], nt;
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
struct node {
int l, r, x;
node(int a = 0, int b = 0, int c = 0) : l(a), r(b), x(c) {}
} val[maxn];
map<node, int> mp;
inline bool operator < (const node &a, const node &b) {
return a.l < b.l || (a.l == b.l && a.x < b.x);
}
inline int newnode(node x) {
int u = ++nt;
val[u] = x;
mp[x] = u;
p[u] = rnd();
sz[u] = 1;
return u;
}
inline void pushup(int x) {
sz[x] = sz[ls[x]] + sz[rs[x]] + 1;
if (ls[x]) {
fa[ls[x]] = x;
}
if (rs[x]) {
fa[rs[x]] = x;
}
}
void split(int u, int k, int &x, int &y) {
if (!u) {
x = y = 0;
return;
}
if (val[u].r <= k) {
x = u;
split(rs[u], k, rs[u], y);
} else {
y = u;
split(ls[u], k, x, ls[u]);
}
pushup(u);
}
int merge(int x, int y) {
if (!x || !y) {
return x + y;
}
if (p[x] < p[y]) {
rs[x] = merge(rs[x], y);
pushup(x);
return x;
} else {
ls[y] = merge(x, ls[y]);
pushup(y);
return y;
}
}
inline int getrt(int x) {
while (fa[x]) {
x = fa[x];
}
return x;
}
inline int query(int x) {
while (rs[x]) {
x = rs[x];
}
return val[x].x;
}
inline int erase(node u) {
int rt = getrt(mp[u]), x, y, z;
split(rt, u.r, x, z);
split(x, u.l - 1, x, y);
return rt = merge(x, z);
}
inline void insert(int &rt, node u) {
int x, y;
split(rt, u.r, x, y);
rt = merge(merge(x, newnode(u)), y);
}
void solve() {
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; ++i) {
S[i].emplace(0, m);
rt[i] = newnode(node(0, m, i));
}
while (q--) {
int op, x, y;
scanf("%d%d", &op, &x);
if (op == 1) {
scanf("%d", &y);
auto i = --S[x].lower_bound(mkp(y, 0)), j = --S[x + 1].lower_bound(mkp(y, 0));
pii p1 = *i, p2 = *j;
int r1 = erase(node(p1.fst, p1.scd, x));
int r2 = erase(node(p2.fst, p2.scd, x + 1));
S[x].erase(i);
S[x + 1].erase(j);
S[x].emplace(p1.fst, y - 1);
S[x].emplace(y, p1.scd);
S[x + 1].emplace(p2.fst, y - 1);
S[x + 1].emplace(y, p2.scd);
insert(r1, node(p1.fst, y - 1, x));
insert(r1, node(y, p1.scd, x));
insert(r2, node(p2.fst, y - 1, x + 1));
insert(r2, node(y, p2.scd, x + 1));
int xa, ya, xb, yb;
split(r1, y - 1, xa, ya);
split(r2, y - 1, xb, yb);
r1 = merge(xa, yb);
r2 = merge(xb, ya);
fa[r1] = fa[r2] = 0;
} else {
pii p = *S[x].begin();
printf("%d\n", query(getrt(mp[node(p.fst, p.scd, x)])));
}
}
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}