zoukankan      html  css  js  c++  java
  • 学习笔记:树分治

    树分治用于解决有关路径的问题。
    树分治分为点分治和边分治(其实还有一种叫“链分治”,是树的路径剖分思想的更高级的体现,一般链分治的题目都可以用路径剖分解决)。点分治就是每次找到重心,然后把重心去掉,对分成的每两棵树之间分别统计路径信息(以重心的每个相邻点为根,遍历整棵子树即可得到这个根到每个结点的统计信息),就可以知道包含这个重心的所有路径的信息,然后对于剩下的路径就是在子树里面进行同样的操作了,直到只剩一个点为止(注意这一个点所构成的路径有时也要处理一下)。边分治就是每次找到一条边,使得删掉这条边后分成的两棵子树大小尽可能平均,然后以删掉的边的两端点为根,分别统计根到两棵树中的每个结点的路径信息,最后合并算路径,即可得到包含这条边的所有路径的信息,剩下的路径在两棵树中递归处理。

    点分治和边分治是可以通用的,不管树上的信息记录在结点上还是边上。这时一个很囧的问题就出现了:这两种分治到底哪个好?这也是本总结讨论的重点。
    有关“哪个好”的问题显然要从三种复杂度上考虑。由于点分治和边分治的空间复杂度均为O(N),因此讨论空间复杂度木有意义。所以需要比较的就是时间复杂度和编程复杂度了。
    先说时间复杂度。点分治每次找到重心后,需要将重心删掉然后分成很多棵子树,对每两棵子树之间都要统计一下。如果操作是可反的(比如计数问题:统计某种路径的条数),可以通过先将所有子树合在一起统计,再减去两端点处在相同子树内的路径,但是,如果操作不可反(比如找“最优路径”),就不能这样做了,只能枚举所有的子树对。显然,如果有S棵子树,则有O(S2)个子树对,最坏情况下(星形的树),S=N-1,此时一一枚举子树对必然会导致总时间复杂度升到O(N2)以上。当然这是有解决办法的,可以从小到大枚举子树,每处理完一棵子树就将其和前面的合并,此时算法效率取决于合并的方法。如果使用归并法(大小为A的和大小为B的合并次数为A+B),对于星形树的合并总次数仍然为O(N2),如果能保证大小为A的和大小为B的合并次数为min{A, B},则可以保证合并总次数在O(N)以内,从而可以保证总时间复杂度为O(NlogN)。边分治由于分成的永远是两棵子树,所以在处理子树之间的关系上能够保证O(N),问题是它并不能保证每次分成的两棵子树大小非常平均,在最坏情况下(星形的树),不管删哪条边分成的两棵子树大小都是一个1一个(N-1),这样就会使总的时间复杂度上升为O(N2)。

    从以上的讨论中可以看出,点分治和边分治最怕的都是星形的树,也就是那种某个点的度数特别大的树。相关论文中提到一种办法通过加无用点的方式将其转化为二叉树,从而解决这个问题。这样一来,点分治和边分治的时间复杂度都可以保证了。接下来是编程复杂度的问题:点分治需要找重心(三次DFS或BFS),删掉重心(需要删点),且分成最多三棵树,需要处理三对关系,代码量肯定大一些,而边分治只需要算出子树大小后就可以找到最优边,并且分成的是两棵树,更重要的是,在有根树上删掉一条边后,分成的两棵树中有一棵已是有根树,另一棵可以通过扭根的方式将其转化为有根树,由于二叉树扭根是很方便的,所以就不需要每次重新建树,代码量较小。综合以上因素,边分治更好些。

    另外,其实那种加无用点转化为二叉树的方式是有问题的,因为它改变了路径的意义,可能导致一条路径由于LCA是无用点而在统计时出现错误(明明是跨越了重心或最优边的路径被当成木有跨越的),但是……这个问题有一种很简单的解决办法,想知道是什么……AC了ALOEXT再告诉你……

    相关题目:
    e.g. (2013年候选队互测•a142857a) 树
    边分治,每次找到所有点到根路径的XOR和,以及特殊点的个数,按照特殊点个数排序后有序插入Trie,查找即可。
    代码:

      1 #include <iostream>
      2 #include <stdio.h>
      3 #include <stdlib.h>
      4 #include <string.h>
      5 #include <algorithm>
      6 using namespace std;
      7 #define re(i, n) for (int i=0; i<n; i++)
      8 #define re1(i, n) for (int i=1; i<=n; i++)
      9 #define re2(i, l, r) for (int i=l; i<r; i++)
     10 #define re3(i, l, r) for (int i=l; i<=r; i++)
     11 #define rre(i, n) for (int i=n-1; i>=0; i--)
     12 #define rre1(i, n) for (int i=n; i>0; i--)
     13 #define rre2(i, r, l) for (int i=r-1; i>=l; i--)
     14 #define rre3(i, r, l) for (int i=r; i>=l; i--)
     15 #define ll long long
     16 const int MAXN = 200010, MAXS = 31, INF = ~0U >> 2;
     17 struct edge {
     18     int a, b, pre, next;
     19 } E[MAXN << 1];
     20 struct sss {
     21     int v1, v2;
     22     bool operator< (sss s0) const {return v1 < s0.v1;}
     23 } S1[MAXN], S2[MAXN];
     24 int T[MAXN * MAXS][2];
     25 int n, m0, K, sum0, __No, N, A[MAXN], Q[MAXN], ls[MAXN], L[MAXN], R[MAXN], pr[MAXN], SZ[MAXN], V1[MAXN], V2[MAXN], res = -1;
     26 bool B[MAXN], vst[MAXN];
     27 void init_d()
     28 {
     29     re(i, n) E[i].pre = E[i].next = i; if (n & 1) m0 = n + 1; else m0 = n;
     30 }
     31 void add_edge(int a, int b)
     32 {
     33     E[m0].a = a; E[m0].b = b; E[m0].pre = E[a].pre; E[m0].next = a; E[a].pre = m0; E[E[m0].pre].next = m0++;
     34     E[m0].a = b; E[m0].b = a; E[m0].pre = E[b].pre; E[m0].next = b; E[b].pre = m0; E[E[m0].pre].next = m0++;
     35 }
     36 void init()
     37 {
     38     scanf("%d%d", &n, &K); init_d();
     39     int x, y;
     40     re(i, n) {scanf("%d", &x); B[i] = x;}
     41     re(i, n) scanf("%d", &A[i]);
     42     re2(i, 1, n) {scanf("%d%d", &x, &y); add_edge(--x, --y);}
     43 }
     44 int dfs0(int l, int r)
     45 {
     46     if (l > r) return -1; else if (l == r) return ls[l]; else {
     47         int n0; if (!l && r == sum0 - 1) n0 = __No; else n0 = n++;
     48         int mid = l + r >> 1, lch = dfs0(l, mid), rch = dfs0(mid + 1, r);
     49         L[n0] = lch; R[n0] = rch; pr[lch] = pr[rch] = n0; return n0;
     50     }
     51 }
     52 void prepare()
     53 {
     54     int x, y; Q[0] = 0; vst[0] = 1; pr[0] = -1;
     55     for (int front=0, rear=0; front<=rear; front++) {
     56         x = Q[front]; sum0 = 0;
     57         for (int p=E[x].next; p != x; p=E[p].next) {
     58             y = E[p].b;
     59             if (!vst[y]) {ls[sum0++] = y; vst[y] = 1; Q[++rear] = y;}
     60         }
     61         if (sum0 > 1) {__No = x; dfs0(0, sum0 - 1);} else if (sum0 == 1) {L[x] = ls[0]; pr[ls[0]] = x; R[x] = -1;} else L[x] = R[x] = -1;
     62     }
     63 }
     64 void solve(int No, int n0)
     65 {
     66     if (n0 == 1) {
     67         if (B[No] >= K && A[No] > res) res = A[No];
     68         return;
     69     }
     70     int x, _x, y, minv = INF, _n0 = n0 >> 1; Q[0] = No;
     71     for (int front=0, rear=0; front<=rear; front++) {
     72         x = Q[front];
     73         if ((y = L[x]) >= 0) Q[++rear] = L[x];
     74         if ((y = R[x]) >= 0) Q[++rear] = R[x];
     75     }
     76     rre(i, n0) {
     77         x = Q[i]; SZ[x] = 1;
     78         if (L[x] >= 0) SZ[x] += SZ[L[x]];
     79         if (R[x] >= 0) SZ[x] += SZ[R[x]];
     80         if (SZ[x] <= _n0 && _n0 - SZ[x] < minv || SZ[x] > _n0 && SZ[x] - _n0 < minv) {minv = SZ[x] <= _n0 ? _n0 - SZ[x] : SZ[x] - _n0; _x = x;}
     81     }
     82     x = pr[_x]; pr[_x] = -1; if (L[x] == _x) L[x] = -1; else R[x] = -1;
     83     int sum0 = 1; ls[0] = x; while ((y = pr[x]) != -1) {if (L[y] == x) L[y] = -1; else R[y] = -1; if (L[x] == -1) L[x] = y; else R[x] = y; x = ls[sum0++] = y;}
     84     pr[ls[0]] = -1; re2(i, 1, sum0) pr[ls[i]] = ls[i - 1];
     85     int len1 = 0, len2 = 0;
     86     Q[0] = _x; V1[_x] = B[_x]; V2[_x] = A[_x]; S1[len1].v1 = V1[_x]; S1[len1++].v2 = V2[_x];
     87     for (int front=0, rear=0; front<=rear; front++) {
     88         x = Q[front];
     89         if ((y = L[x]) >= 0) {Q[++rear] = y; V1[y] = V1[x] + B[y]; V2[y] = V2[x] ^ A[y]; S1[len1].v1 = V1[y]; S1[len1++].v2 = V2[y];}
     90         if ((y = R[x]) >= 0) {Q[++rear] = y; V1[y] = V1[x] + B[y]; V2[y] = V2[x] ^ A[y]; S1[len1].v1 = V1[y]; S1[len1++].v2 = V2[y];}
     91     }
     92     Q[0] = ls[0]; V1[ls[0]] = B[ls[0]]; V2[ls[0]] = A[ls[0]]; S2[len2].v1 = V1[ls[0]]; S2[len2++].v2 = V2[ls[0]];
     93     for (int front=0, rear=0; front<=rear; front++) {
     94         x = Q[front];
     95         if ((y = L[x]) >= 0) {Q[++rear] = y; V1[y] = V1[x] + B[y]; V2[y] = V2[x] ^ A[y]; S2[len2].v1 = V1[y]; S2[len2++].v2 = V2[y];}
     96         if ((y = R[x]) >= 0) {Q[++rear] = y; V1[y] = V1[x] + B[y]; V2[y] = V2[x] ^ A[y]; S2[len2].v1 = V1[y]; S2[len2++].v2 = V2[y];}
     97     }
     98     sort(S1, S1 + len1); sort(S2, S2 + len2); int _len2 = len2 - 1;
     99     N = 1; T[1][0] = T[1][1] = 0; int v0, _v;
    100     re(i, len1) {
    101         while (_len2 >= 0 && S1[i].v1 + S2[_len2].v1 >= K) {
    102             v0 = S2[_len2--].v2; x = 1;
    103             rre(j, MAXS) {
    104                 y = (v0 & (1 << j)) > 0;
    105                 if (!T[x][y]) {T[x][y] = ++N; T[N][0] = T[N][1] = 0;}
    106                 x = T[x][y];
    107             }
    108         }
    109         if (T[1][0] || T[1][1]) {
    110             v0 = S1[i].v2; x = 1; _v = 0;
    111             rre(j, MAXS) {
    112                 y = (v0 & (1 << j)) > 0; _v <<= 1;
    113                 if (T[x][!y]) {x = T[x][!y]; _v++;} else x = T[x][y];
    114             }
    115             if (_v > res) res = _v;
    116         }
    117     }
    118     x = _x; y = ls[0];
    119     solve(x, len1); solve(y, len2);
    120 }
    121 void pri()
    122 {
    123     printf("%d
    ", res);
    124 }
    125 int main()
    126 {
    127     init();
    128     prepare();
    129     solve(0, n);
    130     pri();
    131     return 0;
    132 }
    View Code
  • 相关阅读:
    HDU 1813 Escape from Tetris
    BZOJ 2276 Temperature
    BZOJ 4499 线性函数
    BZOJ 3131 淘金
    HDU 5738 Eureka
    POJ 2409 Let it Bead
    POJ 1286 Necklace of Beads
    POJ 1696 Space Ant
    Fox And Jumping
    Recover the String
  • 原文地址:https://www.cnblogs.com/SBSOI/p/5641621.html
Copyright © 2011-2022 走看看