在一个有 2C 2C 个点,3C2 3C - 2 条边的 2 2 C C 列的网格图中,相邻两个节点有连边,给定以下三种操作。

  1. Open r1 c1 r2 c2 连接节点 (r1, c1), (r2, c2) 的边连通。
  2. Close r1 c1 r2 c2 连接节点 (r1, c1), (r2, c2) 的边断开。
  3. Ask r1 c1 r2 c2 查询节点 (r1, c1), (r2, c2) 之间的连通性。

开始时每条边都是断开的状态。

题解

为什么这种特殊的要死的图我第一时间想到的是 LCT,怕不是我失了智。。

但是看了题解的线段树做法,我感觉我整个人都没有智商了。。怕不是我变成了大傻子。。

原图中有上下两行,这里用 (i,0) (i, 0) 表示第 i i 列上面的城市,用 (i,1) (i, 1) 表示第 i i 列下面的城市。

对于线段树中的一个代表区间 [l,r] [l, r] 的节点,其中维护的是 (l,0),(l,1) (l, 0), (l, 1) 分别与 (r,0),(r,1) (r, 0), (r, 1) 的连通性。

对于某个区间 [l=i,r=i] [l = i, r = i] ,我们认为这个区间中的 (l,0),(r,0) (l, 0), (r, 0) 是连通的,同样的 (l,1),(r,1) (l, 1), (r, 1) 也是连通的。

若该位置上下连通,则认为 (l,0),(r,1) (l, 0), (r, 1) 连通,同时 (r,0),(l,1) (r, 0), (l, 1) 连通。

合并两个区间 [l,m],[m+1,r] [l, m], [m + 1, r] ,枚举 mm+1 m \leftrightarrow m + 1 经过的是上面的还是下面的路径。

回答询问时,假设 c1 c1 c2 c2 左边,二分 c1 c1 能够达到的最左位置以及 c2 c2 能够达到的最右位置,枚举回答。假如 (r1, c1) 不和 (r2, c2) 直接连通,那么这样做可以包含到 (r1, c1) 先向左走以及 (r2, c2) 先向右走的情况。

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
const int MAX_C = 100000;
int n;
bool right[MAX_C][2], uad[MAX_C + 1];
struct Connectivity {
bool a[2][2];
Connectivity(bool init) {
a[0][0] = a[0][1] = a[1][0] = a[1][1] = init;
}
bool &operator()(const int i, const int j) { return a[i][j]; }
bool operator()(const int i, const int j) const { return a[i][j]; }
operator bool() const { return a[0][0] || a[0][1] || a[1][0] || a[1][1]; }
};
inline Connectivity merge(Connectivity a, Connectivity b, int mid) {
Connectivity res(false);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++) {
res(i, j) |= a(i, k) && right[mid][k] && b(k, j);
}
}
}
return res;
}
struct Segt {
int l, r, mid;
Segt *lc, *rc;
// con(0, 0) : (l, 0) <-> (r, 0)
// con(0, 1) : (l, 0) <-> (r, 1)
// con(1, 0) : (l, 1) <-> (r, 0)
// con(1, 1) : (l, 1) <-> (r, 1)
Connectivity con;
Segt(int l, int r, Segt *lc, Segt *rc) : l(l), r(r), mid(l + (r - l) / 2), lc(lc), rc(rc), con(l == r) {}
void modify(int l, int r) {
if (l > this->r || r < this->l) return;
else if (this->l == this->r) {
con(0, 0) = con(1, 1) = true;
con(0, 1) = con(1, 0) = uad[mid];
return;
} else lc->modify(l, r), rc->modify(l, r);
con = merge(lc->con, rc->con, mid);
}
Connectivity query(int l, int r) {
if (l <= this->l && r >= this->r) return this->con;
else if (r <= mid) return lc->query(l, r);
else if (l > mid) return rc->query(l, r);
else return merge(lc->query(l, r), rc->query(l, r), mid);
}
static Segt *build(int l, int r) {
if (l > r) return NULL;
if (l == r) return new Segt(l, r, NULL, NULL);
else {
int mid = l + (r - l) / 2;
return new Segt(l, r, build(l, mid), build(mid + 1, r));
}
}
} *segt;
inline void modify(int r1, int c1, int r2, int c2, bool state) {
if (r1 == r2) {
right[std::min(c1, c2)][r1] = state;
} else if (c1 == c2) {
uad[c1] = state;
}
segt->modify(std::min(c1, c2), std::max(c1, c2));
}
inline bool query(int r1, int c1, int r2, int c2) {
int l, r;
l = 1, r = c1;
while (l < r) {
int mid = l + (r - l) / 2;
Connectivity res = segt->query(mid, c1);
if (res(0, r1) || res(1, r1)) r = mid;
else l = mid + 1;
}
const int lpos = l;
Connectivity lcon = segt->query(lpos, c1);
l = c2, r = n;
while (l < r) {
int mid = l + (r - l) / 2 + 1;
Connectivity res = segt->query(c2, mid);
if (res(r2, 0) || res(r2, 1)) l = mid;
else r = mid - 1;
}
const int rpos = l;
Connectivity rcon = segt->query(c2, rpos);
Connectivity con = segt->query(lpos, rpos);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
if (lcon(i, r1) && rcon(r2, j) && con(i, j)) return true;
}
}
return false;
}
int main() {
scanf("%d", &n);
segt = Segt::build(1, n);
char cmd[5];
while (scanf("%s", cmd) != EOF) {
if (cmd[0] == 'E') break;
else {
int r1, c1, r2, c2;
scanf("%d %d %d %d", &r1, &c1, &r2, &c2);
r1--, r2--;
if (cmd[0] == 'O') modify(r1, c1, r2, c2, true);
else if (cmd[0] == 'C') modify(r1, c1, r2, c2, false);
else {
if (c1 > c2) std::swap(r1, r2), std::swap(c1, c2);
puts(query(r1, c1, r2, c2) ? "Y" : "N");
}
}
}
return 0;
}