1 条题解
-
0
题意可以转化为支持删边,询问两个点是否在同一个边双连通分量里。
删边不好操作,考虑离线转为加边。
发现边双联通就相当于是一个环,因此我们考虑用 维护一棵树,然后对于一次加边操作,如果不联通就是直接 ,否则说明这条路径上的点都在同一个边双里,可以直接将路径上的边赋值为 ,然后 的时候先判是否联通,如果联通就判路径上是否都为 ,正确性不难证明。
同时反向加边的时候记得开 ,但是这题好像数据水不开也能过。
# include <cstdio> # include <set> # include <vector> # include <utility> # include <algorithm> using std::swap; using std::minmax; typedef std::pair<int, int> pii; const int MAXN = 1e5 + 5; int n, m, q, totN; std::multiset<pii> mp; struct Edge { int x, y; } e[MAXN << 1]; struct QQ { int op, x, y; QQ() {} QQ(int O, int X, int Y) { op = O, x = X, y = Y; } }; std::vector<QQ> Q; namespace LCT { struct Splay { int son[2], fa; int siz, sum, val; bool rev, tag; } tr[MAXN << 1]; bool Get(int u) { return tr[ tr[u].fa ].son[1] == u; } void pushup(int u) { int ls = tr[u].son[0], rs = tr[u].son[1]; tr[u].siz = tr[ls].siz + tr[rs].siz + 1; tr[u].sum = tr[ls].sum + tr[rs].sum + tr[u].val; } bool isRt(int u) { return tr[ tr[u].fa ].son[0] != u && tr[ tr[u].fa ].son[1] != u; } void updRev(int u, bool flg) { if (!flg) return; swap(tr[u].son[0], tr[u].son[1]), tr[u].rev ^= 1; } void updCov(int u, bool flg) { if (!flg) return; tr[u].val = 1, tr[u].sum = tr[u].siz, tr[u].tag = true; } void pushdown(int u) { int ls = tr[u].son[0], rs = tr[u].son[1]; if (ls) updCov(ls, tr[u].tag), updRev(ls, tr[u].rev); if (rs) updCov(rs, tr[u].tag), updRev(rs, tr[u].rev); tr[u].rev = tr[u].tag = false; } void pushall(int u) { if (!isRt(u)) pushall(tr[u].fa); pushdown(u); } void rotate(int u) { int fa = tr[u].fa, Gf = tr[fa].fa; bool x = Get(u), y = Get(fa); if (!isRt(fa)) tr[Gf].son[y] = u; tr[u].fa = Gf; tr[fa].son[x] = tr[u].son[x ^ 1], tr[ tr[fa].son[x] ].fa = fa; tr[u].son[x ^ 1] = fa, tr[fa].fa = u; pushup(fa), pushup(u); } void splay(int u) { pushall(u); while (!isRt(u)) { int fa = tr[u].fa; if (!isRt(fa)) rotate(Get(u) ^ Get(fa) ? u : fa); rotate(u); } } void access(int u) { int tmp = u; for (int v = 0; u; v = u, u = tr[u].fa) splay(u), tr[u].son[1] = v, pushup(u); splay(tmp); } void mkRt(int u) { access(u), updRev(u, true); } int findRt(int u) { access(u); while (tr[u].son[0]) pushdown(u), u = tr[u].son[0]; return splay(u), u; } void link(int u, int v) { mkRt(u), tr[u].fa = v; } void split(int u, int v) { mkRt(u), access(v); } void modify(int u, int v) { if (findRt(u) != findRt(v)) return link(u, ++n), link(n, v); split(u, v), updCov(v, true); } bool query(int u, int v) { if (findRt(u) != findRt(v)) return false; split(u, v); return tr[v].siz == tr[v].sum; } } using LCT::modify; using LCT::query; int main() { // freopen("2.in", "r", stdin); // freopen("std.out", "w", stdout); scanf("%d %d %d", &n, &m, &q); for (int i = 1; i <= m; ++i) { scanf("%d %d", &e[i].x, &e[i].y), mp.emplace(minmax(e[i].x, e[i].y)); } for (int i = 1; i <= q; ++i) { char op[10]; int x, y; scanf("%s %d %d", op, &x, &y); switch (op[0]) { case 'Z' : Q.emplace_back(1, x, y); if (mp.find(minmax(x, y)) != mp.end()) mp.erase(mp.find(minmax(x, y))); break; case 'P' : Q.emplace_back(2, x, y); break; default : puts(">_<"); } } for (auto [x, y] : mp) modify(x, y); std::reverse(Q.begin(), Q.end()); std::vector<bool> vec; for (QQ x : Q) { switch (x.op) { case 1 : modify(x.x, x.y); break; case 2 : vec.push_back(query(x.x, x.y)); break; default : puts(">_<"); } } std::reverse(vec.begin(), vec.end()); for (bool x : vec) puts(x ? "Yes" : "No"); return 0; }
- 1
信息
- ID
- 4229
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- (无)
- 递交数
- 21
- 已通过
- 1
- 上传者