给定一颗 N 个节点的有根树,初始时每个叶子节点都有一个不一样的颜色。定义一个节点的代价为其走到根遇到的不同颜色种数。有 M 次询问,共有三种类型:
1.将节点 u 到根的路径上的所有点的颜色改成一种新的颜色。
2.将树根改为节点 u ,同时将两个根之间的路径上所有点改为一种颜色。
3.查询节点 u 子树里所有节点代价的平均数。
N,M≤106
我们先考虑一个子问题,假设没有第二个操作,我们应该怎么维护?
如果没有第二个操作,那么树的根是确定的,也就是说树的DFS序是确定的。我们发现对于第一种操作很像 LCT 中的 Access 操作。所以我们考虑用 LCT 来维护。那么模型就可以转化为:虚边的权值为1,实边的权值为0,初始时全部变都是实边,一个节点 u 的代价就是到遇到的虚边数+1。那么每次 Access 操作我们就需要把一些虚边改成实边,把一些实边改成虚边。
1.对于把虚边改成实边:在 Access 时假设当前已合并的平衡树中最上端的点的父亲为 v ,如果 v 的下方有节点也处于 v 所在的平衡树中,那么这棵平衡树内就要把一条实边转成虚边,也就是把 v 下端,最上方的点打上 +1 的 tag 。
2.对于把实边改成虚边:就是 Access 每次扩大平衡树时都会把一条虚边改成实边,那么只要把当前平衡树中最上端的点打上 −1 的 tag 。
至于维护区间和,只要以DFS序为下标用线段树维护。(这就是CC MONOPLOY)。
那么我们考虑又换个操作的情况,如果有了换根操作,那么DFS序就会乱,线段树维护的值就会有问题。那么我们考虑不改变DFS序,只是在打 tag 的是后考虑一下在原树中根 root 与当前结点 v 的关系。
1. v=root :查询整棵树。
2. v 在 root 的子树内或 x 和 root 不存在包含关系:直接查询原DFS区间。
3. root 在 v 的子树内。设 p 为 v 的所有儿子中是 root 祖先的那个儿子。那么此时 v 的子树应为原树中 v 的子树外所有的节点、 v 本身以及 v 中非 p 的所有儿子的子树。
那么每次查询和修改再加以判断一下就可以维护换根操作。
//YxuanwKeith
#include
#include
#include
using namespace std;
const int MAXN = 2e5;
typedef long long LL;
//struct Tree {int Tag; LL Sum;} Tr[MAXN * 4];
int N, Q, cnt, Root, D[MAXN], L[MAXN], R[MAXN], Deep[MAXN], Ord[MAXN], Tag[MAXN * 4], Fa[MAXN][20];
int tot, Next[MAXN * 2], Last[MAXN], Go[MAXN * 2], Pre[MAXN], Son[MAXN][2], Rev[MAXN];
bool Flag[MAXN];
LL Sum[MAXN * 4];
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 Reverse(int u) {
if (Rev[u]) {
Rev[Son[u][0]] ^= 1, Rev[Son[u][1]] ^= 1;
swap(Son[u][0], Son[u][1]);
Rev[u] = 0;
}
}
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) {
Tag[Now] += Val, Sum[Now] += LL(r - l + 1) * Val;
}
void Push(int x, int l, int r) {
int Mid = (l + r) >> 1;
Update(x * 2, l, Mid, Tag[x]), Update(x * 2 + 1, Mid + 1, r, Tag[x]);
Tag[x] = 0;
}
void Modify(int x, int l, int r, int lx, int rx, int Val) {
if (lx > rx) return;
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);
Sum[x] = Sum[x * 2] + Sum[x * 2 + 1];
}
LL Query(int x, int l, int r, int lx, int rx) {
if (lx > rx) return 0;
if (l == lx && r == rx) return Sum[x];
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);
}
int Chk(int l, int r) {
return max(r - l + 1, 0);
}
int GetDel(int Now, int Goal) {
for (int i = 19; i + 1; i --)
if (Deep[Fa[Now][i]] > Deep[Goal]) Now = Fa[Now][i];
return Now;
}
double Query(int u) {
LL Ans = 0;
int Num = 0;
if (u == Root || L[Root] < L[u] || L[Root] > R[u]) Ans += Query(1, 1, N, L[u], R[u]), Num += Chk(L[u], R[u]); else {
Ans += Query(1, 1, N, 1, L[u] - 1), Num += Chk(1, L[u] - 1);
Ans += Query(1, 1, N, R[u] + 1, N), Num += Chk(R[u] + 1, N);
int Del = GetDel(Root, u);
Ans += Query(1, 1, N, L[u], L[Del] - 1), Num += Chk(L[u], L[Del] - 1);
Ans += Query(1, 1, N, R[Del] + 1, R[u]), Num += Chk(R[Del] + 1, R[u]);
}
return 1.0 * Ans / (1.0 * Num);
}
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) {
static int D[MAXN], top;
D[top = 1] = Now;
for (int p = Now; !IsRoot(p); p = Pre[p]) D[++ top] = Pre[p];
for (; top; top --) Reverse(D[top]);
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) {
Reverse(Now);
for (; Son[Now][0]; Now = Son[Now][0], Reverse(Now));
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]);
if (S == Root || L[Root] < L[S] || L[Root] > R[S]) Modify(1, 1, N, L[S], R[S], 1); else {
Modify(1, 1, N, 1, L[S] - 1, 1);
Modify(1, 1, N, R[S] + 1, N, 1);
int Del = GetDel(Root, S);
Modify(1, 1, N, L[S], L[Del] - 1, 1);
Modify(1, 1, N, R[Del] + 1, R[S], 1);
}
};
if (t) {
int S = GetSon(t);
if (S == Root || L[Root] < L[S] || L[Root] > R[S]) Modify(1, 1, N, L[S], R[S], -1); else {
Modify(1, 1, N, 1, L[S] - 1, -1);
Modify(1, 1, N, R[S] + 1, N, -1);
int Del = GetDel(Root, S);
Modify(1, 1, N, L[S], L[Del] - 1, -1);
Modify(1, 1, N, R[Del] + 1, R[S], -1);
}
}
}
}
void Build(int x, int l, int r) {
if(l == r) {
Sum[x] = Deep[Ord[l]] + 1;
return;
}
int Mid = (l + r) >> 1;
Build(x * 2, l, Mid), Build(x * 2 + 1, Mid + 1 ,r);
Sum[x] = Sum[x * 2] + Sum[x * 2 + 1];
}
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;
Fa[v][0] = u;
}
} 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 --;
}
}
for (int j = 1; j < 20; j ++)
for (int i = 1; i <= N; i ++)
Fa[i][j] = Fa[Fa[i][j - 1]][j - 1];
Build(1, 1, N);
}
void MakeRoot(int u) {
Access(u), Splay(u);
Rev[u] ^= 1, Root = u;
}
int main() {
scanf("%d%d", &N, &Q);
for (int i = 1; i < N; i ++) {
int u, v;
Read(u), Read(v);
Link(u, v), Link(v, u);
}
Preparation();
Root = 1;
for (int i = 1; i <= Q; i ++) {
int u, Ord;
Read(Ord), Read(u);
if (Ord == 3) printf("%.8f\n", Query(u));
if (Ord == 1) Access(u);
if (Ord == 2) MakeRoot(u);
}
}