【NOI复习】树链剖分

简介

  树链剖分通常用来解决一类维护静态树上路径信息的问题, 例如:
给定一棵点带权树, 接下来每次操作会修改某条路径上所有点的权值(修改为同一个值或是同加上一个值等) , 以及询问某条路径上所有点的权值和。
当这棵树是一条链时, 这个问题实际上就是一个序列上区间修改、 区间询问的问题, 可以用之前介绍的几个数据结构解决。
对于其他情况, 由于树的形态是不变的, 因此树链剖分的策略是将这些点按某种方式组织起来, 剖分成为若干条链, 每条链就相当于一个序列, 则操作路径可以拆分为剖分好的某几条链, 也就是若干个完整序列或是某个序列上的一段区间, 此时再利用线段树等处理序列上区间操作问题的数据结构来解决。
树链剖分的核心就是如何恰当的剖分树为若干条链。 当链的划分方式确定后, 我们只要将它们看做是一个个序列, 将所有序列按顺序拼接起来后, 每条链就成为了一段区间, 而序列上的区间问题是我们所熟悉和擅长解决的。

方法

轻重链剖分

我们将树中的边分成两种: 轻边, 重边。 如下图中加粗的边是重边, 其余是轻边。

 

我们可以以任意点为根, 然后记 size(u) 为以 u 为根的子树的结点个数, 令 v u 所有儿子中 size 值最大的一个儿子, 则(u,v) 为重边, v 称为u 的重儿子。 u 到其余儿子的边为
轻边。

 

树链剖分求LCA

  传送门

 

例题

【浙江省选2008】树的统计

 

题目背景

 

ZJOI2008 DAY1 T4

 

题目描述

 

一棵树上有 n 个节点,编号分别为 1 到 n ,每个节点都有一个权值 w 。
我们将以下面的形式来要求你对这棵树完成一些操作:
I.CHANGE u t :把结点 u 的权值改为 t ;
II.QMAX u v :询问从点 u 到点 v 的路径上的节点的最大权值;
III.QSUM u v :询问从点 u 到点 v 的路径上的节点的权值和。

注意:从点 u 到点 v 的路径上的节点包括 u 和 v 本身。

 

输入格式

 

输入第一行为一个整数 n ,表示节点的个数。
接下来 n–1 行,每行 2 个整数 a 和 b ,表示节点 a 和节点 b 之间有一条边相连。
接下来 n 行,每行一个整数,第 i 行的整数 wi 表示节点 i 的权值。
接下来 1 行,为一个整数 q ,表示操作的总数。
接下来 q 行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。

 

输出格式

 

对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

 

样例数据 1

 

输入


1 2 
2 3 
4 1 
4 2 1 3 
12 
QMAX 3 4 
QMAX 3 3 
QMAX 3 2 
QMAX 2 3 
QSUM 3 4 
QSUM 2 1 
CHANGE 1 5 
QMAX 3 4 
CHANGE 3 6 
QMAX 3 4 
QMAX 2 4 
QSUM 3 4

输出





10 




16

 

备注

 

【数据范围】

对于 100% 的数据,保证1<=n<=30000;0<=q<=200000;中途操作中保证每个节点的权值 w 在 -30000 到 30000 之间。

【题目分析】

 

模板题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
 
const int N = 30050;
const int oo = 0x3f3f3f3f;
 
int dep[N], sze[N], top[N], son[N], pos[N], idx[N], val[N], fa[N];
int ecnt, adj[N], go[N << 1], nxt[N << 1], tot;
int sum[N * 4], maxx[N * 4];
int n, q;
 
inline int Re(){
    int i = 0, f = 1; char ch = getchar();
    for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
    if(ch == '-') f = -1, ch = getchar();
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        i = (i << 3) + (i << 1) + (ch - '0');
    return i * f;
}
 
inline void Wr(int x){
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) Wr(x / 10);
    putchar(x % 10 + '0');
}
 
inline void addEdge(const int &u, const int &v){
    nxt[++ecnt] = adj[u], adj[u] = ecnt, go[ecnt] = v;
    nxt[++ecnt] = adj[v], adj[v] = ecnt, go[ecnt] = u;
}
 
inline void dfs1(const int &u, const int &f){
    dep[u] = dep[f] + 1;
    fa[u] = f;
    sze[u] = 1;
    for(int e = adj[u]; e; e = nxt[e]){
        int v = go[e];
        if(v == f) continue;
        dfs1(v, u);
        sze[u] += sze[v];
        if(sze[v] > sze[son[u]]) son[u] = v;
    }
}
 
inline void dfs2(const int &u, const int &f){
    if(son[u]){   //先查重儿子, 保证重链连续
        top[son[u]] = top[u];
        idx[pos[son[u]] = ++tot] = son[u];  
        dfs2(son[u], u);
    }
    for(int e = adj[u]; e; e = nxt[e]){
        int v = go[e];
        if(v == f || v == son[u]) continue;
        top[v] = v;
        idx[pos[v] = ++tot] = v;
        dfs2(v, u);
    }
}
 
inline int chkMax(const int &x, const int &y){
    if(x > y) return x;
    return y;
}
 
inline void build(int k, int l, int r){
    if(l == r){
        sum[k] = maxx[k] = val[idx[l]];
        return;
    }
    int mid = l + r >> 1, lc = k << 1, rc = k << 1 | 1;
    build(lc, l, mid);
    build(rc, mid + 1, r);
    sum[k] = sum[lc] + sum[rc];
    maxx[k] = chkMax(maxx[lc], maxx[rc]);
}
 
inline int PathSum(int k, int l, int r, int x, int y){
    if(x <= l && r <= y) return sum[k];
    int mid = l + r >> 1, lc = k << 1, rc = k << 1 | 1;
    int ret = 0;
    if(x <= mid) ret += PathSum(lc, l, mid, x, y);
    if(y > mid) ret += PathSum(rc, mid + 1, r, x, y);
    return ret;
}
 
inline int PathMax(int k, int l, int r, int x, int y){
    if(x <= l && r <= y) return maxx[k];
    int mid = l + r >> 1, lc = k << 1, rc = k << 1 | 1;
    int ret = -oo;
    if(x <= mid) ret = chkMax(ret, PathMax(lc, l, mid, x, y));
    if(y > mid) ret = chkMax(ret, PathMax(rc, mid + 1, r, x, y));
    return ret;
}
 
inline void PrintSum(int u, int v){
    int ret = 0;
    while(top[u] != top[v]){
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        ret += PathSum(1, 1, n, pos[top[u]], pos[u]);
        u = fa[top[u]];
    }
    if(dep[u] > dep[v]) swap(u, v);
    ret += PathSum(1, 1, n, pos[u], pos[v]);
    Wr(ret), putchar('\n');
}
 
inline void PrintMax(int u, int v){
    int ret = -oo;
    while(top[u] != top[v]){
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        ret = chkMax(ret, PathMax(1, 1, n, pos[top[u]], pos[u]));
        u = fa[top[u]];
    }
    if(dep[u] > dep[v]) swap(u, v);
    ret = chkMax(ret, PathMax(1, 1, n, pos[u], pos[v]));
    Wr(ret), putchar('\n');
}
 
inline void modify(int k, int l, int r, int pos, int v){
    if(l == r){
        sum[k] = v;
        maxx[k] = v;
        return;
    }
    int mid = l + r >> 1, lc = k << 1, rc = k << 1 | 1;
    if(pos <= mid) modify(lc, l, mid, pos, v);
    else modify(rc, mid + 1, r, pos, v);
    sum[k] = sum[lc] + sum[rc];
    maxx[k] = chkMax(maxx[lc], maxx[rc]);
}
 
inline void print(int k){
    if(k == 0) return;
    print(k<<1);print(k<<1|1);
    cout<<sum[k]<<" "<<maxx[k]<<endl;
}
 
int main(){
//    freopen("h.in", "r", stdin);
    n = Re();
    for(int i = 1; i < n; i++){
        int a = Re(), b = Re();
        addEdge(a, b);
    }
    for(int i = 1; i <= n; i++) val[i] = Re();
     
    dep[0] = -1, top[1] = 1, idx[1] = 1, pos[1] = 1, tot = 1;
    dfs1(1, 0);
    dfs2(1, 0);
    build(1, 1, n);
//    print(1);
     
    q = Re();
    for(int i = 1; i <= q; i++){
        char opt[20]; int u, v, t;
        scanf("%s", opt + 1);
        if(opt[2] == 'H'){   //change
            u = Re(), t = Re();
            modify(1, 1, n, pos[u], t);
        }
        else if(opt[2] == 'M'){   //qmax
            u = Re(), v = Re();
            PrintMax(u, v);
        }
        else//qsum
            u = Re(), v = Re();
            PrintSum(u, v);
        }
    }
}
View Code

 

 【bzoj2243】【山东省选2011】染色

Description

给定一棵有n个节点的无根树和m个操作,操作有2类:

1、将节点a到节点b路径上所有点都染成颜色c;

2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”3段组成:“11”、“222”和“1”

请你写一个程序依次完成这m个操作。

Input

第一行包含2个整数n和m,分别表示节点数和操作数;

第二行包含n个正整数表示n个节点的初始颜色

下面 行每行包含两个整数x和y,表示xy之间有一条无向边。

下面 行每行描述一个操作:

“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;

“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。

Output

对于每个询问操作,输出一行答案。

Sample Input

6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5

Sample Output

3
1
2

HINT

数N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。

【题目分析】

 

 树链剖分,维护节点的颜色段数, 修改标记, 左端、右端颜色, 注意用左右子树更新根节点时颜色相同要-1, 数组线段树不好维护可以写成结构体!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
 
const int N = 3e5;
 
int n, m;
int sze[N], dep[N], val[N], idx[N], pos[N], fa[N], top[N], son[N], tot;
int ecnt, adj[N], go[N << 1], nxt[N << 1];
 
inline int Re(){
    int i = 0, f = 1; char ch = getchar();
    for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
    if(ch == '-') f = -1, ch = getchar();
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        i = (i << 3) + (i << 1) + (ch - '0');
    return i * f;
}
 
inline void Wr(int x){
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) Wr(x / 10);
    putchar(x % 10 + '0');
}
 
inline void addEdge(const int &u, const int &v){
    nxt[++ecnt] = adj[u], adj[u] = ecnt, go[ecnt] = v;
    nxt[++ecnt] = adj[v], adj[v] = ecnt, go[ecnt] = u;
}
 
inline void dfs1(const int &u, const int &f){
    dep[u] = dep[f] + 1;
    sze[u] = 1;
    fa[u] = f;
    for(int e = adj[u]; e; e = nxt[e]){
        int v = go[e];
        if(v == f) continue;
        dfs1(v, u);
        sze[u] += sze[v];
        if(sze[v] > sze[son[u]]) son[u] = v;
    }
}
 
inline void dfs2(const int &u, const int &f){
    if(son[u]){
        top[son[u]] = top[u];
        idx[pos[son[u]] = ++tot] = son[u];
        dfs2(son[u], u);
    }
    for(int e = adj[u]; e; e = nxt[e]){
        int v = go[e];
        if(v == f || v == son[u]) continue;
        top[v] = v;
        idx[pos[v] = ++tot] = v;
        dfs2(v, u);
    }
}
 
struct node{
    int cnt, lcol, rcol, tag;
    node():cnt(0), lcol(-1), rcol(-1), tag(-1){}
};
 
namespace SegTree{
    node tr[N * 4];
    inline void upt(int k){
        int lc = k << 1, rc = k << 1 | 1;
        tr[k].lcol = tr[lc].lcol;
        tr[k].rcol = tr[rc].rcol;
        tr[k].cnt = tr[lc].cnt + tr[rc].cnt - (tr[lc].rcol == tr[rc].lcol);
    }
    inline void cover(int k, int v){
        tr[k].lcol = tr[k].rcol = v;
        tr[k].cnt = 1;
        tr[k].tag = v;
    }
    inline void pushDown(int k){
        int lc = k << 1, rc = k << 1 | 1;
        if(tr[k].tag != -1){
            cover(lc, tr[k].tag);
            cover(rc, tr[k].tag);
            tr[k].cnt = 1, tr[k].lcol = tr[k].rcol = tr[k].tag;
            tr[k].tag = -1;
        }
    }
     
    inline void build(int k, int l, int r){
        if(l == r){
            tr[k].cnt = 1;
            tr[k].tag = -1;
            tr[k].lcol = tr[k].rcol = val[idx[l]];
            return;
        }
        int mid = l + r >> 1, lc = k << 1, rc = k << 1 | 1;
        build(lc, l, mid);
        build(rc, mid + 1, r);
        upt(k);
    }
     
    inline void modify(int k, int l, int r, int x, int y, int v){
        if(x <= l && r <= y){
            cover(k, v);
            return;
        }
        pushDown(k);
        int mid = l + r >> 1, lc = k << 1, rc = k << 1 | 1;
        if(x <= mid) modify(lc, l, mid, x, y, v);
        if(y > mid) modify(rc, mid + 1, r, x, y, v);
        upt(k);
    }
     
    inline node query(int k, int l, int r, int x, int y){
        if(l == x && r == y) return tr[k];
        pushDown(k);
        int mid = l + r >> 1, lc = k << 1, rc = k << 1 | 1;
        if(y <= mid) return query(lc, l, mid, x, y);
        else if(x > mid) return query(rc, mid + 1, r, x, y);
        else {
            node ret, ret1, ret2;
            ret1 = query(lc, l, mid, x, mid);
            ret2 = query(rc, mid + 1, r, mid + 1, y);
            ret.cnt = ret1.cnt + ret2.cnt - (ret1.rcol == ret2.lcol);
            ret.lcol = ret1.lcol, ret.rcol = ret2.rcol;
            return ret;
        }
//        cout<<ret1.lcol<<" "<<ret1.lcol<<" "<<ret2.lcol<<" "<<ret2.rcol<<endl;   
         
    }
}using namespace SegTree;
inline void PrintCnt(int a, int b){
    int ans = 0, acol = -1, bcol = -1;
    while(top[a] != top[b]){
        if(dep[top[a]] < dep[top[b]]) swap(a, b), swap(acol, bcol);
        node ret = query(1, 1, n, pos[top[a]], pos[a]);
        ans += ret.cnt;
        if(ret.rcol == acol) ans--;
        a = fa[top[a]], acol = ret.lcol;
    }
    if(dep[a] > dep[b]) swap(a, b), swap(acol, bcol);
    node ret = query(1, 1, n, pos[a], pos[b]);
    ans += ret.cnt - (ret.lcol == acol) - (ret.rcol == bcol);
    Wr(ans);
}
 
inline void PathModify(int a, int b, int v){
    while(top[a] != top[b]){
        if(dep[top[a]] < dep[top[b]]) swap(a, b);
        modify(1, 1, n, pos[top[a]], pos[a], v);
        a = fa[top[a]];
    }
    if(dep[a] > dep[b]) swap(a, b);
    modify(1, 1, n, pos[a], pos[b], v);
}
 
int main(){
    freopen("h.in", "r", stdin);
    n = Re(), m = Re();
    for(int i = 1; i <= n; i++) val[i] = Re();
    for(int i = 1; i < n; i++){
        int a = Re(), b = Re();
        addEdge(a, b);
    }
    dep[0] = -1, top[1] = pos[1] = idx[1] = tot = 1;
    dfs1(1, 0);
    dfs2(1, 0);
    build(1, 1, n);
    for(int i = 1; i <= m; i++){
        char opt; opt = getchar();
        while(opt != 'Q' && opt != 'C') opt = getchar();
        int a, b, c;
        if(opt == 'C'){
            a = Re(), b = Re(), c = Re();
            PathModify(a, b, c);
        }
        else if(opt == 'Q'){
            a = Re(), b = Re();
            PrintCnt(a, b), putchar('\n');
        }
    }
    return 0;
}
View Code

 

posted @   CzYoL  阅读(306)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示