Solution
- 首先发现把 \(2\) 操作都丢到最后处理不会影响答案
- 那么可以把所有修改操作拆成在 \(l\) 处加入,在 \(r+1\) 处删除
- 把所有操作读入之后按树的编号顺序处理
- 动态维护一棵树,即处理第 \(i\) 棵树后,这棵树就是第 \(i\) 棵树的结构
- 但是不维护原树结构,而是维护一个等价的东西:
- 给每个 \(1\) 操作建一个虚点,每个 \(0\) 操作(也可以说是原树上的每个点)建一个实点
- 实点的父亲始终不变(所以一开始就连好),都是在它之前的第一个 \(1\) 操作的虚点
- 处理第 \(i\) 棵树后,如果虚点 \(x\) 对应的操作在这棵树上生效,那么 \(x\) 的父亲为它对应的实点,否则 \(x\) 向上一个虚点连边
- 一开始可以看作执行一个把所有树的生长节点都改为 \(1\) 的操作,因此一开始多建一个虚点,它的父亲是实点 \(1\)
- 然后一开始还要把所有虚点都连向上一个
- 构建多余的实点不会影响答案,但 \(1\) 操作 (\(1\) \(l\) \(r\) \(x\)) 的区间 \([l,r]\) 里不一定所有树都长过编号为 \(x\) 的节点,但是长过编号为 \(x\) 的节点的树一定构成一个区间 \([u,v]\),所以 \([l,r]\) 和 \([u,v]\) 取并集即可
- 操作 \(0\) 的影响也只有这些,不用拆成 \(l\) 和 \(r+1\)
- 操作 \(1\) 在 \(l\) 处加入:把对应虚点连向对应实点
- 在 \(r+1\) 处删除:把对应虚点连回上一个虚点
- 对于第 \(i\) 棵树,处理好所有关于它的修改之后即可查询答案
- 查询答案:记 \(s(x)\) 为 \(x\) 到根路径上点权之和,显然实点权值为 \(1\),虚点为 \(0\)
- 那么 \(ans=s(x)+s(y)-s(lca(x,y))\)
- 注意此题中不能 \(makeroot\),因为不能打乱父子关系,树边是有向的
- 找 \(lca\):先 \(access(x)\),然后 \(access(y)\)
- \(access(y)\) 的循环结束条件:已经到了和根同一条实链上,然后不是有个 \(rc[u]=v\) 这样的东西吗,此时的 \(u\) 即 \(lca(x,y)\)
- 好像不太好描述清楚,那么上代码:
inline int access(int x)
{
int y;
for (y = 0; x; y = x, x = fa[x])
{
splay(x);
rc[x] = y;
if (y) fa[y] = x;
upt(x);
}
return y;
}
- 可以维护每个点 \(splay\) 上的子树点权和,查询 \(s(x)\) 只要 \(splay(x)\) 然后用自己点权加上左子树点权之和
Code
#include <bits/stdc++.h>
using namespace std;
template <class t>
inline void read(t & res)
{
char ch;
while (ch = getchar(), !isdigit(ch));
res = ch ^ 48;
while (ch = getchar(), isdigit(ch))
res = res * 10 + (ch ^ 48);
}
template <class t>
inline void print(t x)
{
if (x > 9) print(x / 10);
putchar(x % 10 + 48);
}
const int e = 1e6 + 5;
struct point
{
int opt, x, y, id;
};
vector<point>g[e];
int lc[e], rc[e], fa[e], num, sum[e], n, m, c1 = 1, c2 = 1, q, ans[e], val[e], lst[e];
int ld[e], rd[e];
inline bool which(int x)
{
return rc[fa[x]] == x;
}
inline bool isroot(int x)
{
return !fa[x] || (lc[fa[x]] != x && rc[fa[x]] != x);
}
inline void upt(int x)
{
sum[x] = val[x];
if (lc[x]) sum[x] += sum[lc[x]];
if (rc[x]) sum[x] += sum[rc[x]];
}
inline void rotate(int x)
{
int y = fa[x], z = fa[y], b;
if (z && !isroot(y)) (lc[z] == y ? lc[z] : rc[z]) = x;
b = (lc[y] == x ? rc[x] : lc[x]);
fa[x] = z;
fa[y] = x;
if (b) fa[b] = y;
if (lc[y] == x)
{
rc[x] = y;
lc[y] = b;
}
else
{
lc[x] = y;
rc[y] = b;
}
upt(y);
upt(x);
}
inline void splay(int x)
{
while (!isroot(x))
{
if (!isroot(fa[x]))
{
if (which(x) == which(fa[x])) rotate(fa[x]);
else rotate(x);
}
rotate(x);
}
}
inline int access(int x)
{
int y;
for (y = 0; x; y = x, x = fa[x])
{
splay(x);
rc[x] = y;
if (y) fa[y] = x;
upt(x);
}
return y;
}
inline void link(int x, int y)
{
lst[x] = y;
splay(x);
fa[x] = y;
}
inline void cut(int x)
{
access(x);
splay(x);
fa[lc[x]] = 0;
lc[x] = 0;
upt(x);
}
inline int lca(int x, int y)
{
access(x);
return access(y);
}
inline int query(int x)
{
access(x);
splay(x);
return val[x] + sum[lc[x]];
}
int main()
{
int i, opt, l, r, x, j;
read(n); read(m);
link(c1 + m, c2);
val[c2] = sum[c2] = 1;
ld[1] = 1;
rd[1] = n;
for (i = 1; i <= m; i++)
{
read(opt);
read(l);
read(r);
if (opt == 0)
{
c2++;
ld[c2] = l;
rd[c2] = r;
val[c2] = sum[c2] = 1;
cut(c2);
link(c2, c1 + m);
}
else if (opt == 1)
{
c1++;
cut(c1 + m);
link(c1 + m, c1 + m - 1);
read(x);
l = max(l, ld[x]);
r = min(r, rd[x]);
if (l > r) continue;
g[l].push_back((point){1, c1 + m, x, 0});
g[r + 1].push_back((point){-1, c1 + m, c1 + m - 1, 0});
}
else
{
read(x);
g[l].push_back((point){2, r, x, ++q});
}
}
point u;
for (i = 1; i <= n; i++)
{
for (auto u : g[i])
if (u.opt == -1 || u.opt == 1)
{
cut(u.x);
link(u.x, u.y);
}
for (auto u : g[i])
if (u.opt == 2) ans[u.id] = query(u.x) + query(u.y) - 2 * query(lca(u.x, u.y));
}
for (i = 1; i <= q; i++)
{
print(ans[i]);
putchar('\n');
}
return 0;
}