可以简单发现, 经过一条边的路径条数就是这条边左边的点数乘以右边的点数。
那么就只要动态维护一棵树两边的点的size
就可以了。
那么就用(整体的点数-子树的点数)就是另外一部分的点数。
在LCT上,儿子有左儿子,右儿子和虚儿子之分。
那么我们在维护的时候就分类讨论一下就可以了。
注意到只有Link
和access
操作会改变虚儿子。 那么我们只要在这个时候维护虚儿子就可以了。
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef long double LD;
int read() {
char ch = getchar();
int x = 0, flag = 1;
for (;!isdigit(ch); ch = getchar()) if (ch == '-') flag *= -1;
for (;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
return x * flag;
}
void write(int x) {
if (x < 0) putchar('-'), x = -x;
if (x >= 10) write(x / 10);
putchar(x % 10 + 48);
}
template <int N> struct LCT {
struct node {
int fa, ch[2], revTag, sumSize, voidSonSize;
}t[N + 1];
int _top, stk[N + 1];
#define fa(x) (t[(x)].fa)
#define lc(x) (t[(x)].ch[0])
#define rc(x) (t[(x)].ch[1])
bool isroot(int u) {
return lc(fa(u)) != u && rc(fa(u)) != u;
}
void pushup(int u) {
t[u].sumSize = t[lc(u)].sumSize + t[rc(u)].sumSize + 1 + t[u].voidSonSize;
}
void setRev(int u) {
t[u].revTag ^= 1, swap(lc(u), rc(u));
}
void pushdown(int u) {
if (t[u].revTag) {
setRev(lc(u)); setRev(rc(u));
t[u].revTag = 0;
}
}
void rotate(int u) {
int y = fa(u), z = fa(y), dir = rc(y) == u;
if (!isroot(y)) t[z].ch[t[z].ch[1] == y] = u; t[u].fa = z;
t[y].ch[dir] = t[u].ch[dir ^ 1]; t[t[u].ch[dir ^ 1]].fa = y;
t[u].ch[dir ^ 1] = y; t[y].fa = u;
pushup(y); pushup(u);
}
void splay(int u) {
stk[_top = 1] = u;
for (int y = u; !isroot(y); y = fa(y)) stk[++_top] = fa(y);
while (_top) pushdown(stk[_top--]);
while (!isroot(u)) {
int y = fa(u), z = fa(y);
if (!isroot(y))
(rc(z) == y) ^ (rc(y) == u) ? rotate(u) : rotate(y);
rotate(u);
}
pushup(u);
}
void access(int u) {
for (int y = 0; u; u = fa(y = u)) {
splay(u);
t[u].voidSonSize += t[rc(u)].sumSize - t[y].sumSize;
t[u].ch[1] = y, pushup(u);
}
}
void makeRoot(int u) {
access(u), splay(u), setRev(u);
}
int findRoot(int u) {
access(u); splay(u);
while (lc(u)) pushdown(u), u = lc(u);
return u;
}
void link(int u, int v) {
makeRoot(u); makeRoot(v);
if (findRoot(u) != v) t[v].fa = u, t[u].voidSonSize += t[v].sumSize, pushup(u);
}
void cut(int u, int v) {
makeRoot(u); access(v); splay(v);
if (findRoot(v) == u && t[v].ch[0] == u && fa(u) == v) {
t[v].ch[0] = t[u].fa = 0;
pushup(v);
}
}
#undef fa
#undef lc
#undef rc
};
const int Maxn = 100009;
LCT <Maxn> s;
int n, q;
void init() {
n = read(); q = read();
}
void solve() {
rep (i, 1, n) s.t[i].sumSize = 1;
rep (i, 1, q) {
char Cl2 = getchar();
int H2SO4 = read(), NaCl = read();
if (Cl2 == 'A') s.link(H2SO4, NaCl);
if (Cl2 == 'Q') {
s.makeRoot(H2SO4), s.makeRoot(NaCl);
LL HCl = 1ll * s.t[H2SO4].sumSize * (1ll * s.t[NaCl].sumSize - s.t[H2SO4].sumSize);
printf("%lld
", HCl);
}
}
}
int main() {
// freopen("LG4219.in", "r", stdin);
// freopen("LG4219.out", "w", stdout);
init();
solve();
#ifdef Qrsikno
debug("
Running time: %.3lf(s)
", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
return 0;
}