bzoj4817: [Sdoi2017]树点涂色 LCT+树链剖分+线段树

bzoj4817: [Sdoi2017]树点涂色

Description

Bob有一棵n个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。定义一条路
径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。Bob可能会进行这几种操作:
1 x:
把点x到根节点的路径上所有的点染上一种没有用过的新颜色。
2 x y:
求x到y的路径的权值。
3 x
在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
Bob一共会进行m次操作

Input

第一行两个数n,m。
接下来n-1行,每行两个数a,b,表示a与b之间有一条边。
接下来m行,表示操作,格式见题目描述
1<=n,m<=100000

Output

每当出现2,3操作,输出一行。
如果是2操作,输出一个数表示路径的权值
如果是3操作,输出一个数表示权值的最大值

Sample Input

5 6
1 2
2 3
3 4
3 5
2 4 5
3 3
1 4
2 4 5
1 5
2 4 5

Sample Output

3
4
2
2

分析

听说过LCT可以用来维护路径,子树信息,还有树边的删除和加入。
但是没听说过能模拟的。
这道就是了。
染上一种新的颜色实际上就是 A c c e s s 操作。
考虑用线段树维护每个点的权值。
这样子的话2操作就是
a n s = v [ x ] + v [ y ] 2 l c a ( x , y ) + 1
这里可以直接这么做的原因是,没有一个节点的两个儿子是同色的,因为 A c c e s s 搞的是一条链。
其实如果不放心的话判断一下也无妨
考虑 A c c e s s 的影响,显然只有跳虚边的时候才有影响。
就是把之前的子树里面的所有节点权值都 + 1 ,新子树里面所有节点权值都 1
线段树搞一搞就好了。
为什么要树剖?求 L c a

代码

#include<cstdio>
#include<algorithm>
#define ls p << 1
#define rs p << 1 | 1
using std::max; using std::min;
const int N = 1e5 + 10;
int ri() {
    char ch = getchar(); int x = 0;
    for(;ch < '0' || ch > '9'; ch = getchar()) ;
    for(;ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) - '0' + ch;
    return x;
}
int fa[N], ch[N][2], f[N], pr[N], pos[N], to[N << 1], nx[N << 1], tp;
int tg[N << 2], mx[N << 2], in[N], out[N], de[N], ds[N], d[N], sz[N], tot, n;
void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;}
void adds(int u, int v) {add(u, v); add(v, u);}
int wh(int p) {return ch[fa[p]][1] == p;}
int Ir(int p) {return ch[fa[p]][1] != p && ch[fa[p]][0] != p;}
void Rotate(int p) {
    int f = fa[p], g = fa[f], c = wh(p);
    if(!Ir(f)) ch[g][wh(f)] = p; fa[p] = g;
    ch[f][c] = ch[p][c ^ 1]; if(ch[f][c]) fa[ch[f][c]] = f;
    ch[p][c ^ 1] = f; fa[f] = p;
}
void Splay(int p) {
    for(;!Ir(p); Rotate(p))
        if(!Ir(fa[p])) Rotate(wh(p) == wh(fa[p]) ? fa[p] : p);
}
void Tag(int p, int v) {tg[p] += v; mx[p] += v;}
void Push(int p) {if(tg[p]) Tag(ls, tg[p]), Tag(rs, tg[p]), tg[p] = 0;}
void Mod(int p, int L, int R, int st, int ed, int v) {
    if(L == st && ed == R) return Tag(p, v);
    int m = L + R >> 1; Push(p); 
    if(st <= m) Mod(ls, L, m, st, min(ed, m), v);
    if(ed > m) Mod(rs, m + 1, R, max(st, m + 1), ed, v);
    mx[p] = max(mx[ls], mx[rs]);
}
void Mod(int u, int v) {if(u) {for(;ch[u][0]; u = ch[u][0]) ; Mod(1, 1, n, in[u], out[u], v);}}
int Mx(int p, int L, int R, int st, int ed) {
    if(L == st && ed == R) return mx[p];
    int m = L + R >> 1, ans = 0; Push(p); 
    if(st <= m) ans = max(ans, Mx(ls, L, m, st, min(ed, m)));
    if(ed > m) ans = max(ans, Mx(rs, m + 1, R, max(st, m + 1), ed));
    return ans;
}
void Access(int p) {
    for(int pr = 0; p; p = fa[pr = p])
        Splay(p), Mod(ch[p][1], 1), ch[p][1] = pr, Mod(pr, -1);
}
void Dfs1(int u, int F) {
    fa[u] = f[u] = F; de[u] = de[F] + 1; sz[u] = 1;
    for(int i = pr[u]; i; i = nx[i]) if(to[i] != F) 
        Dfs1(to[i], u), sz[u] += sz[to[i]], ds[u] = sz[ds[u]] > sz[to[i]] ? ds[u] : to[i];
}
void Dfs2(int u, int rt) {
    pos[in[u] = ++tot] = u; d[u] = rt; if(!ds[u]) return void(out[u] = tot); Dfs2(ds[u], rt);
    for(int i = pr[u]; i; i = nx[i]) if(to[i] != f[u] && to[i] != ds[u]) Dfs2(to[i], to[i]);
    out[u] = tot;
}
int Que(int u, int v) {
    int rt = Mx(1, 1, n, in[u], in[u]) + Mx(1, 1, n, in[v], in[v]);
    for(;d[u] != d[v]; u = f[d[u]]) if(de[d[u]] < de[d[v]]) std::swap(u, v);
    if(de[u] > de[v]) std::swap(u, v);
    return rt - (Mx(1, 1, n, in[u], in[u]) << 1) + 1;
}
void Build(int p, int L, int R) {
    if(L == R) return void(mx[p] = de[pos[L]]); int m = L + R >> 1;
    Build(ls, L, m); Build(rs, m + 1, R); mx[p] = max(mx[ls], mx[rs]);
}
int main() {
    n = ri(); int m = ri();
    for(int i = 1;i < n; ++i) adds(ri(), ri());
    Dfs1(1, 0); Dfs2(1, 1); Build(1, 1, n);
    for(int op, x;m--;) {
        op = ri(), x = ri();
        if(op == 1) Access(x);
        if(op == 2) printf("%d\n", Que(x, ri()));
        if(op == 3) printf("%d\n", Mx(1, 1, n, in[x], out[x]));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/80684867