给你 N 个点组成的一棵树,标号为 0 ~ N−1 且编号为 0 的点为根。每个点都有一种颜色,一开始 N 个点的颜色都不相同。
距离的定义:如果两个相邻的节点颜色一样则两点间的距离为 0 ,否则为 1 。
现在给你 Q 个操作,每个操作给你一个字符和一个数字 Ord,u 。
Ord=O :把编号为 u 的点到根路径上的所有点染成一种新的颜色。
Ord=q :询问以 u 为根的子树中的所有节点到根的距离和。
N,Q<=150000
首先我们可以发现,相同颜色的节点一定是连续一段的一条链,且方向是朝向根的。那么一个显然的性质就是对于一个节点,最多只会有一个儿子的颜色与它相同。那现在就讨论一下修改颜色时情况。
我们不妨设当前节点为 u ,他的父亲节点为 v ,节点i的颜色为 Coli 。
若 Colu=Colv ,那么显然对答案是没有影响的。
若 Colu≠Colv , 假设 u 有一个的儿子 p ,满足 Colu=Colp ,那么会产生影响的就只有 p,v 两颗子树。我们再用以颗线段树维护每个节点到根的距离。那么这个操作就是对 u,p 的整颗子树 +1,−1 。当我们用 DFS 序表示时就变成区间修改了。那么到目前我们就处理完了一个个往上跳的情况。
那么怎么快速的修改呢。看到这种修改某个节点到根路径上所有点信息时,我们和自然的就想到了 LCT 的 Access 操作。那我们是否可以用 LCT 来维护呢?
我们惊讶地发现,在 LCT 中一颗 Splay 上的节点肯定是被 Access 过的,他们的颜色肯定是相等的。也就是说我们一颗 Splay 中只需要处理最下面的一个节点就可以了。所以在 Access 时,每次对合并两颗 Splay ,只有相交处的节点需要修改,做上述的区间修改操作就可以快速完成了。
//CodeChef MONOPOLY YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 2e5;
typedef long long LL;
struct Tree {int Tag; LL Sum;} Tr[MAXN * 4];
int N, Q, cnt, D[MAXN], L[MAXN], R[MAXN], Deep[MAXN], Ord[MAXN];
int tot, Next[MAXN * 2], Last[MAXN], Go[MAXN * 2], Pre[MAXN], Son[MAXN][2];
bool Flag[MAXN];
void Read(int &Now) {
char c;
while (c = getchar(), c < '0' || c > '9');
Now = c - 48;
while (c = getchar(), c >= '0' && c <= '9') Now = Now * 10 + c - 48;
}
void Link(int u, int v) {
Next[++ tot] = Last[u], Last[u] = tot, Go[tot] = v;
}
void Update(int Now, int l, int r, int Val) {
Tr[Now].Tag += Val, Tr[Now].Sum += LL(r - l + 1) * Val;
}
void Push(int x, int l, int r) {
int Mid = (l + r) >> 1;
Update(x * 2, l, Mid, Tr[x].Tag), Update(x * 2 + 1, Mid + 1, r, Tr[x].Tag);
Tr[x].Tag = 0;
}
void Modify(int x, int l, int r, int lx, int rx, int Val) {
if (l == lx && r == rx) {
Update(x, l, r, Val);
return;
}
Push(x, l, r);
int Mid = (l + r) >> 1;
if (rx <= Mid) Modify(x * 2, l, Mid, lx, rx, Val); else
if (lx > Mid) Modify(x * 2 + 1, Mid + 1, r, lx, rx, Val); else
Modify(x * 2, l, Mid, lx, Mid, Val), Modify(x * 2 + 1, Mid + 1, r, Mid + 1, rx, Val);
Tr[x].Sum = Tr[x * 2].Sum + Tr[x * 2 + 1].Sum;
}
LL Query(int x, int l, int r, int lx, int rx) {
if (l == lx && r == rx) return Tr[x].Sum;
Push(x, l, r);
int Mid = (l + r) >> 1;
if (rx <= Mid) return Query(x * 2, l, Mid, lx, rx); else
if (lx > Mid) return Query(x * 2 + 1, Mid + 1, r, lx, rx); else
return Query(x * 2, l, Mid, lx, Mid) + Query(x * 2 + 1, Mid + 1, r, Mid + 1, rx);
}
bool IsRoot(int Now) {return Son[Pre[Now]][0] != Now && Son[Pre[Now]][1] != Now;}
void Rotate(int Now) {
int Fa = Pre[Now], Gran = Pre[Fa], Side = (Son[Fa][1] == Now);
if (!IsRoot(Fa)) Son[Gran][Son[Gran][1] == Fa] = Now;
Pre[Now] = Gran, Pre[Fa] = Now;
Son[Fa][Side] = Son[Now][!Side], Pre[Son[Fa][Side]] = Fa;
Son[Now][!Side] = Fa;
}
void Splay(int Now) {
for (; !IsRoot(Now); Rotate(Now)) {
int Fa = Pre[Now], Gran = Pre[Fa];
if (IsRoot(Fa)) continue;
(Son[Fa][1] == Fa) ^ (Son[Gran][1] == Fa) ? Rotate(Now) : Rotate(Fa);
}
}
int GetSon(int Now) {
for (; Son[Now][0]; Now = Son[Now][0]);
return Now;
}
void Access(int Now) {
for (int t = 0; Now; Son[Now][1] = t, t = Now, Now = Pre[Now]) {
Splay(Now);
if (Son[Now][1]) {
int S = GetSon(Son[Now][1]);
Modify(1, 1, N, L[S], R[S], 1);
};
if (t) {
int S = GetSon(t);
Modify(1, 1, N, L[S], R[S], -1);
}
}
}
void Build(int x, int l, int r) {
if (l == r) {
Tr[x].Sum = Deep[Ord[l]];
return;
}
int Mid = (l + r) >> 1;
Build(x * 2, l, Mid), Build(x * 2 + 1, Mid + 1, r);
Tr[x].Sum = Tr[x * 2].Sum + Tr[x * 2 + 1].Sum;
}
void Preparation() {
int top = 1; D[1] = 1;
while (top) {
int u = D[top];
if (!Flag[u]) {
Flag[u] = 1, L[u] = R[u] = ++ cnt; Ord[cnt] = u;
for (int p = Last[u]; p; p = Next[p]) {
int v = Go[p];
if (L[v]) continue;
D[++ top] = v, Pre[v] = u, Deep[v] = Deep[u] + 1;
}
} else {
for (int p = Last[u]; p; p = Next[p])
if (L[Go[p]] > L[u]) R[u] = max(R[u], R[Go[p]]);
top --;
}
}
Build(1, 1, N);
}
int main() {
scanf("%d", &N);
for (int i = 1; i < N; i ++) {
int u, v;
Read(u), Read(v);
u ++, v ++;
Link(u, v), Link(v, u);
}
Preparation();
scanf("%d", &Q);
for (int i = 1; i <= Q; i ++) {
char c; int u;
while (c = getchar(),c != 'q' && c != 'O');
Read(u);
u ++;
if (c == 'q') printf("%.8f\n", Query(1, 1, N, L[u], R[u]) / double(R[u] - L[u] + 1)); else Access(u);
}
}