JZOJ4753【GDOI2017模拟9.4】种树 LCT维护子树信息+换根时维护Dfs序(CC MONOPLOY加强版)

来源:互联网 发布:steam联机游戏mac 编辑:程序博客网 时间:2024/05/24 06:37

题目大意

给定一颗N个节点的有根树,初始时每个叶子节点都有一个不一样的颜色。定义一个节点的代价为其走到根遇到的不同颜色种数。有M次询问,共有三种类型:
1.将节点u到根的路径上的所有点的颜色改成一种新的颜色。
2.将树根改为节点u,同时将两个根之间的路径上所有点改为一种颜色。
3.查询节点u子树里所有节点代价的平均数。

N,M106

解题思路

我们先考虑一个子问题,假设没有第二个操作,我们应该怎么维护?

如果没有第二个操作,那么树的根是确定的,也就是说树的DFS序是确定的。我们发现对于第一种操作很像LCT中的Access操作。所以我们考虑用LCT来维护。那么模型就可以转化为:虚边的权值为1,实边的权值为0,初始时全部变都是实边,一个节点u的代价就是到遇到的虚边数+1。那么每次Access操作我们就需要把一些虚边改成实边,把一些实边改成虚边。

1.对于把虚边改成实边:在Access时假设当前已合并的平衡树中最上端的点的父亲为v,如果v的下方有节点也处于v所在的平衡树中,那么这棵平衡树内就要把一条实边转成虚边,也就是把v下端,最上方的点打上+1tag

2.对于把实边改成虚边:就是Access每次扩大平衡树时都会把一条虚边改成实边,那么只要把当前平衡树中最上端的点打上1tag

至于维护区间和,只要以DFS序为下标用线段树维护。(这就是CC MONOPLOY)。

那么我们考虑又换个操作的情况,如果有了换根操作,那么DFS序就会乱,线段树维护的值就会有问题。那么我们考虑不改变DFS序,只是在打tag的是后考虑一下在原树中根root与当前结点v的关系。

1.v=root:查询整棵树。

2.vroot的子树内或xroot不存在包含关系:直接查询原DFS区间。

3.rootv的子树内。设pv的所有儿子中是root祖先的那个儿子。那么此时v的子树应为原树中v的子树外所有的节点、v本身以及v中非p的所有儿子的子树。

那么每次查询和修改再加以判断一下就可以维护换根操作。

程序

//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, 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);    }}
1 0