Solution
- \(LCT\),对每个节点 \(u\) 维护两个信息:\(s[u], si[u]\)
- 记 \(sze[u]\) 为实子树的大小,即 \(splay\) 上的子树大小
- 令 \(s[u]=si[u]+sze[u]\)
- \(si[u]\) :虚子树的大小之和,即所有虚儿子的 \(s\) 之和
- 如果 \(u\) 是所在重链的根,那么 \(s[u]\) 即 \(u\) 原树(即题目中所给的树结构)上的子树大小
- 对于边 \((x,y)\),如果执行 \(split(x,y)\),那么 \((si[x]+1) * (si[y] + 1)\) 即答案
- 考虑如何维护 \(s\) 和 \(si\)
- 显然初始所有 \(s[u] = 1,si[u]=0\)
- 改变虚儿子会改变 \(si[u]\),改变实儿子和虚儿子都会改变 \(s[u]\)
- 也就是说 \(rotate,access,link\) 都会改变至少一个信息
- 对于 \(rotate\),和维护其它信息一样,旋转之后 \(upt/pushup\) 即可
- 对于 \(access\),给出如下代码:
inline void access(int x)
{
for (int y = 0; x; y = x, x = fa[x])
{
splay(x);
si[x] += s[rc[x]];
si[x] -= s[y];
rc[x] = y;
if (y) fa[y] = x;
}
}
- 可以看出,\(x\) 原来的实儿子变虚儿子,\(y\) 所在重链的根由虚儿子变实儿子,所以要修改 \(si[x]\),但是 \(s[x]\) 不用修改,因为两个儿子虚实互换,实儿子 \(+\) 虚儿子大小不会改变
- 对于 \(link\),给出如下代码
inline void link(int x, int y)
{
makeroot(x);
access(y);
splay(y);
fa[x] = y;
si[y] += s[x];
upt(y);
}
- 可以看出,\(y\) 多出来了一个实儿子 \(x\),所以要修改 \(si[y]\) 和 \(s[y]\)
- 重点是,要先 \(access(y), splay(y)\),因为 \(si[y]\) 和 \(s[y]\) 的修改会导致 \(y\) 的祖先信息发生变化,所以让 \(y\) 和根合到一个 \(splay\) 中并将 \(y\) 转到根,即让 \(fa[y] = 0\),就没有祖先受到影响了
- 现在问题来了,刚才的 \(access\) 中修改了 \(si\) ,是否也要考虑对祖先的影响呢?其实不用,观察这段代码可知,每次都 \(splay(x)\),也就是不用考虑对同一 \(splay\) 中的祖先的影响了。而且只修改 \(si[x]\),对不同 \(splay\) 中的祖先没有任何影响
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);
}
const int e = 1e5 + 5;
int n, m, lc[e], s[e], si[e], rc[e], rev[e], fa[e], q[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 pushdown(int x)
{
if (rev[x])
{
if (lc[x]) rev[lc[x]] ^= 1;
if (rc[x]) rev[rc[x]] ^= 1;
swap(lc[x], rc[x]);
rev[x] = 0;
}
}
inline void upt(int x)
{
s[x] = s[lc[x]] + s[rc[x]] + 1 + si[x];
}
inline void rotate(int x)
{
int y = fa[x], z = fa[y];
pushdown(y);
pushdown(x);
int b = (lc[y] == x ? rc[x] : lc[x]);
if (z && !isroot(y)) (lc[z] == y ? lc[z] : rc[z]) = 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)
{
int w;
q[w = 1] = x;
for (int y = x; !isroot(y); y = fa[y]) q[++w] = fa[y];
for (int i = w; i >= 1; i--) pushdown(q[i]);
while (!isroot(x))
{
if (!isroot(fa[x]))
{
if (which(x) == which(fa[x])) rotate(fa[x]);
else rotate(x);
}
rotate(x);
}
}
inline void access(int x)
{
for (int y = 0; x; y = x, x = fa[x])
{
splay(x);
si[x] += s[rc[x]];
si[x] -= s[y];
rc[x] = y;
if (y) fa[y] = x;
}
}
inline void makeroot(int x)
{
access(x);
splay(x);
rev[x] ^= 1;
}
inline void link(int x, int y)
{
makeroot(x);
access(y);
splay(y);
fa[x] = y;
si[y] += s[x];
upt(y);
}
inline void split(int x, int y)
{
makeroot(x);
access(y);
splay(y);
}
int main()
{
int i, x, y;
char ch;
read(n); read(m);
for (i = 1; i <= n; i++) s[i] = 1;
while (m--)
{
while (ch = getchar(), !isalpha(ch));
read(x);
read(y);
if (ch == 'A') link(x, y);
else
{
split(x, y);
printf("%lld\n", 1ll * (si[x] + 1) * (si[y] + 1));
}
}
return 0;
}