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)繼承某個兒子的信息

因此可維護的信息種類相對有限

相關文章
相關標籤/搜索