[POI 2021/2022 R2] kon 题解
前言
题目链接:洛谷。
题意简述
维护一张无向图图,初始两个点,通过一条边相连。\(q\) 次操作:
W x:新建点,和 \(x\) 连边。Z x:新建点,和 \(x\) 所有直接相连的点连边。? x:查询有几个点和 \(x\) 直接相连。
题目分析
妙妙题。
发现 Z 操作很像把 \(x\) 复制一遍,相当于一个新的版本。提示我们对操作建树,\(u\) 继承了 \(\operatorname{fa}(u)\) 所有对应的连边状态。
那么 W 操作就是先把 \(x\) 复制一遍,在新版 \(x\) 上与新建点「连边」(这里连边是原图上相连的意思,并非树边,称之为「W 边」),这样不会让之前的点错误继承这次操作。
原图上,两个点 \(u,v\) 相连,当且仅当操作树上 \(u\) 的某一个祖先 \(u'\) 和 \(v\) 的某一个祖先 \(v'\),通过 W 边相连。
那么问题变为了求 \(x\) 的每一个祖先,所有连出 W 边指向的点,在操作树上的子树中有几个点,这里需要对点做区分,不能算上 W 操作复制的版本。
稍微分类讨论一下。对于树根,相当于单点加子树和;对于非树根,修改的时候将树根 W 边父亲单点修改,链查询。
均可以树状数组做到 \(\mathcal{O}(n\log n)\)。
可以 LCT 做到在线,相信大家都会。
代码
离线树状数组
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
const int N = 1e6 + 10;
const int M = N * 2;
int tot, n, q, qid;
enum QType { ADD, SUB, QUERY };
struct {
QType op;
int u;
} qry[N * 3];
int rl[N]; // u 实际在操作树上的点(最新版本)
int root[M], Wfa[M], fa[M];
vector<int> e[M];
inline int newNode() {
return rl[++tot] = ++n;
}
inline void linkW(int u) {
int x = rl[u];
e[x].emplace_back(rl[u] = ++n); // 新建版本
qry[++qid] = { SUB, x };
qry[++qid] = { ADD, rl[u] };
fa[rl[u]] = x;
int v = newNode();
qry[++qid] = { ADD, v };
Wfa[v] = rl[u];
}
inline void linkZ(int u) {
int v = newNode();
qry[++qid] = { ADD, v };
e[rl[u]].emplace_back(v);
fa[v] = rl[u];
}
int dfn[M], timer, R[M];
void dfs(int u) {
dfn[u] = ++timer;
for (int v : e[u]) root[v] = root[u], dfs(v);
R[u] = timer;
}
struct {
int t[M];
inline void upd(int p, int v) {
for (; p <= n; p += p & -p) t[p] += v;
}
inline void upd(int l, int r, int v) {
upd(l, v), upd(r + 1, -v);
}
inline int qry(int p) {
int r = 0;
for (; p; p &= p - 1) r += t[p];
return r;
}
inline int qry(int l, int r) {
return qry(r) - qry(l - 1);
}
} t[2];
signed main() {
rl[1] = 1, tot = n = 1;
qry[++qid] = { ADD, 1 };
linkW(1);
scanf("%d", &q);
for (int u; q--; ) {
char s[2];
scanf("%s%d", s, &u);
if (*s == 'W') linkW(u);
else if (*s == 'Z') linkZ(u);
else qry[++qid] = { QUERY, rl[u] };
}
for (int i = 1; i <= n; ++i)
if (!dfn[i]) {
root[i] = i;
dfs(i);
}
for (int i = 1; i <= qid; ++i) {
int u = qry[i].u;
if (qry[i].op != QUERY) {
int v = qry[i].op == SUB ? -1 : 1;
t[0].upd(dfn[u], v);
u = Wfa[root[u]];
if (u) t[1].upd(dfn[u], R[u], v);
} else {
int r = t[1].qry(dfn[u]);
u = Wfa[root[u]];
if (u) r += t[0].qry(dfn[u], R[u]);
printf("%d\n", r);
}
}
return 0;
}
在线 LCT
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 2e6 + 10;
#define ls s[0]
#define rs s[1]
struct {
int f, s[2];
int v, tg, w, sm;
bool r;
} t[N];
inline bool isR(int x) {
return x != t[t[x].f].ls && x != t[t[x].f].rs;
}
inline bool get(int x) {
return x == t[t[x].f].rs;
}
inline void U(int x) {
t[x].sm = t[t[x].ls].sm + t[x].w + t[t[x].rs].sm;
}
inline void Ro(int x) {
int y = t[x].f, z = t[y].f;
bool o = get(x);
if (!isR(y)) t[z].s[get(y)] = x;
t[x].f = z;
t[t[y].s[o] = t[x].s[!o]].f = y;
t[t[x].s[!o] = y].f = x;
U(y), U(x);
}
inline void P(int u, int v) {
if (u) t[u].tg += v, t[u].v += v;
}
inline void R(int u) {
if (u) t[u].r ^= 1, swap(t[u].ls, t[u].rs);
}
inline void PD(int u) {
if (t[u].r) R(t[u].ls), R(t[u].rs), t[u].r = false;
if (t[u].tg) P(t[u].ls, t[u].tg), P(t[u].rs, t[u].tg), t[u].tg = 0;
}
inline void D(int u) {
if (!isR(u)) D(t[u].f);
PD(u);
}
inline void S(int u) {
D(u);
for (int v = t[u].f; !isR(u); Ro(u), v = t[u].f)
if (!isR(v)) Ro(get(u) == get(v) ? v : u);
}
inline int A(int u) {
int o = 0;
while (u)
S(u), t[u].rs = o, U(u), o = u, u = t[u].f;
return o;
}
inline void MR(int u) {
R(A(u));
}
inline void link(int u, int v) {
t[v].f = u;
}
#undef ls
#undef rs
int tot, n, q;
int rl[N], Wfa[N], RT[N];
void AD(int u, int w) {
MR(RT[u]);
P(A(u), w);
u = Wfa[RT[u]];
if (u) S(u), t[u].w += w, t[u].sm += w;
}
inline int gen() {
return rl[++tot] = ++n;
}
inline void linkW(int u) {
int x = rl[u];
rl[u] = ++n;
RT[rl[u]] = RT[x];
AD(x, -1);
link(x, rl[u]);
AD(rl[u], 1);
int v = gen();
RT[v] = v;
Wfa[v] = rl[u];
AD(v, 1);
}
inline void linkZ(int u) {
int v = gen();
RT[v] = RT[rl[u]];
link(rl[u], v);
AD(v, 1);
}
signed main() {
rl[1] = 1, RT[1] = 1, tot = n = 1, AD(1, 1);
linkW(1);
scanf("%d", &q);
for (int u; q--; ) {
char s[2];
scanf("%s%d", s, &u);
if (*s == 'W') linkW(u);
else if (*s == 'Z') linkZ(u);
else {
u = rl[u];
MR(RT[u]);
int r = t[A(u)].sm;
u = Wfa[RT[u]];
if (u) S(u), r += t[u].v;
printf("%d\n", r);
}
}
return 0;
}
本文作者:XuYueming,转载请注明原文链接:https://www.cnblogs.com/XuYueming/p/18937211。
若未作特殊说明,本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。

浙公网安备 33010602011771号