dsu+树链剖分+树分治

dsu,对于无修改子树信息查询,而且操做支持undo的问题ios

暴力dfs,对于每一个节点,对全部轻儿子dfs下去,而后再消除轻儿子的影响c++

dfs重儿子,而后dfs暴力恢复轻儿子们的影响,再把当前节点影响算进去算法

就有了整棵子树的信息了,时间复杂度O(nlogn)ide

经典例题:http://codeforces.com/contest/600/problem/Espa

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 typedef long long ll;
 6 
 7 const int N = 1e5 + 5;
 8 
 9 int n, c[N];
10 
11 int cnt[N], maxCnt;
12 
13 int siz[N], son[N];
14 
15 vector <int> e[N];
16 
17 ll ans[N], sum[N];
18 
19 void dfs1(int u, int fr) {
20     siz[u] = 1;
21     for (int v : e[u]) {
22         if (v == fr) continue;
23         dfs1(v, u);
24         siz[u] += siz[v];
25         if (siz[v] > siz[son[u]]) son[u] = v;
26     }
27 }
28 
29 void update(int x, int y) {
30     sum[cnt[x]] -= x;
31     cnt[x] += y;
32     sum[cnt[x]] += x;
33     if (cnt[x] > maxCnt) maxCnt = cnt[x];
34     if (sum[maxCnt] == 0) maxCnt --;
35 }
36 
37 void dfs3(int u, int fr, int val) {
38     update(c[u], val);
39     for (int v : e[u]) {
40         if (v == fr) continue;
41         dfs3(v, u, val);
42     }
43 }
44 
45 void dfs2(int u, int fr) {
46     for (int v : e[u]) {
47         if (v == fr || v == son[u]) continue;
48         dfs2(v, u), dfs3(v, u, -1);
49     }
50     if (son[u]) dfs2(son[u], u);
51     for (int v : e[u]) {
52         if (v == fr || v == son[u]) continue;
53         dfs3(v, u, 1);
54     }
55     update(c[u], 1);
56     ans[u] = sum[maxCnt];
57 }
58 
59 int main() {
60     ios::sync_with_stdio(false);
61     cin >> n;
62     for (int i = 1; i <= n; i ++)
63         cin >> c[i];
64     for (int u, v, i = 1; i < n; i ++) {
65         cin >> u >> v;
66         e[u].push_back(v);
67         e[v].push_back(u);
68     }
69     dfs1(1, 1), dfs2(1, 1);
70     for (int i = 1; i <= n; i ++)
71         cout << ans[i] << ' ';    
72     cout << endl;
73     return 0;
74 }
View Code

 

长链剖分,选择深度大的儿子做为重儿子code

O(1)继承重儿子信息,而后按深度合并轻儿子信息blog

由于每一个节点被做为轻链节点只会被合并一次,因此O(n)继承

例题:http://codeforces.com/problemset/problem/1009/Fci

 1 /* 长链剖分,选择深度最大的儿子做为重儿子,用于合并以深度为下标的信息
 2  * 像 dsu 同样,直接继承重儿子信息,而后按深度暴力合并其余儿子信息
 3  * 时间复杂度考虑每一个节点做为轻儿子里的节点被合并只会有一次,因此 O(n)
 4  * 另外一种用法,能够 O(nlogn) 预处理后,O(1) 找到 k 级祖先
 5  */
 6 int n;
 7 int len[N], son[N], ans[N];
 8 vector <int> e[N];
 9 int tmp[N], *ptr, *f[N];
10 void dfs(int u, int fr) {
11     for (int v : e[u]) {
12         if (v == fr) continue;
13         dfs(v, u);
14         if (len[v] > len[son[u]]) son[u] = v;
15     }
16     len[u] = len[son[u]] + 1;
17 }
18 void dp(int u, int fr) {
19     f[u][0] = 1;
20     if (son[u]) {
21         f[son[u]] = f[u] + 1;
22         dp(son[u], u);
23         ans[u] = ans[son[u]] + 1;
24     }
25     for (int v : e[u]) {
26         if (v == son[u] || v == fr) continue;
27         f[v] = ptr, ptr += len[v];
28         dp(v, u);
29         for (int j = 0; j < len[v]; j ++) {
30             f[u][j + 1] += f[v][j];
31             if ((f[u][j + 1] > f[u][ans[u]]) || (f[u][j + 1] == f[u][ans[u]] && j + 1 < ans[u]))
32                 ans[u] = j + 1;
33         }
34     }
35     if (f[u][0] >= f[u][ans[u]]) ans[u] = 0;
36 }
37 int main() {
38     in(n);
39     for (int u, v, i = 1; i < n; i ++) {
40         in(u), in(v);
41         e[u].push_back(v);
42         e[v].push_back(u);
43     }
44     dfs(1, 1);
45     f[1] = ptr = tmp, ptr += len[1];
46     dp(1, 1);
47     for (int i = 1; i <= n; i ++)
48         printf("%d\n", ans[i]);
49     return 0;
50 }
View Code

 

区分几种算法(dsu,树链剖分,树分治)用途:get

树链剖分分为重链剖分和长链剖分

重链剖分应用比较多也比较常见再也不赘述

固然dsu虽然也是一种应用但仍是拿出来提一下把

下面的树分治仅仅针对点分治

 

dsu,长链剖分,点分治

三种都是无修改的树上信息查询算法

dsu应用限制:

只能统计子树中全部点的信息,而且操做必须支持删除

因此没法维护链的信息

长链剖分应用限制:

由于通常用于基于深度的信息合并

因此没法维护子树所有信息,只能维护深度相关信息

因此对于有边权的树通常都没有办法

树分治应用限制:

多用来树上路径的统计计数

缺点是没法像上述两种算法O(1)继承某个儿子的信息

因此可维护的信息种类相对有限

相关文章
相关标签/搜索