• BZOJ 3510


    这题 FlashHu 的优化思路值得借鉴

    前置引理

    • 树中所有点到某个点的距离和中,到重心的距离和是最小的。

    • 把两棵树通过某一点相连得到一颗新的树,新的树的重心必然在连接原来两棵树重心的路径上。

    • 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。

    • 一棵树最多有两个重心,且相邻;同时,拥有奇数个节点的树只有一个重心

    • 其实是树的重心本身的定义:各个子树大小皆不超过总节点数的一半的节点即为树的重心(证明:不管向哪一侧移动,对应的子树节点个数都是 $le$ 树的总节点一半的,也就是说,剩下的节点数 $ge$ 总节点数的一半,故该点为中心)

    题解

    根据引理 $1$ ,题目很明显是要求维护树的重心

    最简单的方法是启发式合并,根据引理 $3, 5$ ,每加一条边,若当前子树大小 $ge$ 总节点数的一半,就将重心往这里移一次,复杂度 $O (n log^2 n)$

    对于优化,根据引理 $2$ ,取出两棵原树在新树上之间的路径,进行类似二分的操作,对于当前查询区间 $[L, p), (p, R]$ ,并同时存储 $L$ 之前的子树节点总和 $lsum$ ,以及 $R$ 之后的 $rsum$ ,且因为 $Splay$ 上是根据中序遍历,所以当前节点在 $Splay$ 上的左右子节点可以代表分割的左右区间,故若 $lsum + size[son[p][0]] le half && size[son[p][1]] + rsum ge half$ ,那么点 $p$ 即为树的一个重心(注意由于在当前 $Splay$ 上 $lsum$ 所属的节点属于实点且已经跳过,故 $size[son[p][0]]$ 不会重复计算到 $lsum$,因为 $LCT$ 中维护的子树为 $Splay$ 中的子树,虚树的维护是一定不会将同一 $Splay$ 的实点加进去的),根据引理 $4$ ,若总节点数为奇数,那么已经可以结束查找了;反之则需继续查找是否有编号更小的,看左右区间哪边子节点多就去哪里就好了,总复杂度 $O (n log n)$

    代码

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstring>
      4 
      5 using namespace std;
      6 
      7 const int MAXN = 1e05 + 10;
      8 
      9 int father[MAXN]= {0};
     10 int son[MAXN][2]= {0};
     11 int subtree[MAXN]= {0}, virsize[MAXN]= {0};
     12 int revtag[MAXN]= {0};
     13 
     14 int isroot (int p) {
     15     return son[father[p]][0] != p && son[father[p]][1] != p;
     16 }
     17 int sonbel (int p) {
     18     return son[father[p]][1] == p;
     19 }
     20 void reverse (int p) {
     21     if (! p)
     22         return ;
     23     swap (son[p][0], son[p][1]);
     24     revtag[p] ^= 1;
     25 }
     26 void pushup (int p) {
     27     subtree[p] = subtree[son[p][0]] + subtree[son[p][1]] + virsize[p] + 1;
     28 }
     29 void pushdown (int p) {
     30     if (revtag[p]) {
     31         reverse (son[p][0]), reverse (son[p][1]);
     32         revtag[p] = 0;
     33     }
     34 }
     35 void rotate (int p) {
     36     int fa = father[p], anc = father[fa];
     37     int s = sonbel (p);
     38     son[fa][s] = son[p][s ^ 1];
     39     if (son[fa][s])
     40         father[son[fa][s]] = fa;
     41     if (! isroot (fa))
     42         son[anc][sonbel (fa)] = p;
     43     father[p] = anc;
     44     son[p][s ^ 1] = fa, father[fa] = p;
     45     pushup (fa), pushup (p);
     46 }
     47 int Stack[MAXN];
     48 int top = 0;
     49 void splay (int p) {
     50     top = 0, Stack[++ top] = p;
     51     for (int nd = p; ! isroot (nd); nd = father[nd])
     52         Stack[++ top] = father[nd];
     53     while (top > 0)
     54         pushdown (Stack[top]), top --;
     55     for (int fa = father[p]; ! isroot (p); rotate (p), fa = father[p])
     56         if (! isroot (fa))
     57             sonbel (p) == sonbel (fa) ? rotate (fa) : rotate (p);
     58 }
     59 void Access (int p) {
     60     for (int tp = 0; p; tp = p, p = father[p])
     61         splay (p), virsize[p] += subtree[son[p][1]] - subtree[tp], son[p][1] = tp, pushup (p);
     62 }
     63 void Makeroot (int p) {
     64     Access (p), splay (p), reverse (p);
     65 }
     66 void Split (int x, int y) {
     67     Makeroot (x);
     68     Access (y), splay (y);
     69 }
     70 void link (int x, int y) {
     71     Split (x, y);
     72     father[x] = y, virsize[y] += subtree[x];
     73     pushup (y);
     74 }
     75 
     76 int ances[MAXN]; // 重心用并查集维护
     77 int find (int p) {
     78     return p == ances[p] ? p : ances[p] = find (ances[p]);
     79 }
     80 
     81 int N, Q;
     82 char opt[5];
     83 
     84 int Newgrvy (int p) {
     85     int half = subtree[p] >> 1;
     86     int isodd = subtree[p] & 1;
     87     int lsum = 0, rsum = 0;
     88     int grvy = N + 1;
     89     while (p) {
     90         pushdown (p);
     91         int l = lsum + subtree[son[p][0]], r = rsum + subtree[son[p][1]];
     92         if (l <= half && r <= half) {
     93             if (p < grvy)
     94                 grvy = p;
     95             if (isodd)
     96                 return grvy;
     97         }
     98         if (l > r) {
     99             rsum += subtree[son[p][1]] + virsize[p] + 1;
    100             p = son[p][0];
    101         }
    102         else {
    103             lsum += subtree[son[p][0]] + virsize[p] + 1;
    104             p = son[p][1];
    105         }
    106     }
    107     splay (grvy);
    108     return grvy;
    109 }
    110 
    111 int getnum () {
    112     int num = 0;
    113     char ch = getchar ();
    114 
    115     while (! isdigit (ch))
    116         ch = getchar ();
    117     while (isdigit (ch))
    118         num = (num << 3) + (num << 1) + ch - '0', ch = getchar ();
    119 
    120     return num;
    121 }
    122 
    123 int ans = 0;
    124 int main () {
    125     N = getnum (), Q = getnum ();
    126     for (int i = 1; i <= N; i ++)
    127         subtree[i] = 1, ances[i] = i, ans ^= i;
    128     for (int i = 1; i <= Q; i ++) {
    129         scanf ("%s", opt + 1);
    130         if (opt[1] == 'A') {
    131             int x = getnum (), y = getnum ();
    132             link (x, y);
    133             int fx = find (x), fy = find (y);
    134             Split (fx, fy);
    135             int grvy = Newgrvy (fy);
    136             ans ^= fx ^ fy ^ grvy;
    137             ances[fx] = ances[fy] = ances[grvy] = grvy;
    138         }
    139         else if (opt[1] == 'Q') {
    140             int p = getnum ();
    141             printf ("%d
    ", find (p));
    142         }
    143         else if (opt[1] == 'X')
    144             printf ("%d
    ", ans);
    145     }
    146 
    147     return 0;
    148 }
    149 
    150 /*
    151 10 10
    152 Xor
    153 Q 1
    154 A 10 1
    155 A 1 4
    156 Q 4
    157 Q 10
    158 A 7 6
    159 Xor
    160 Q 7
    161 Xor
    162 */
  • 相关阅读:
    个人总结21
    个人总结08
    个人总结07
    构建之法读后感01
    学习进度表 03
    四则运算3
    求最大值
    学习进度表02
    四则运算 2
    学习进度表01
  • 原文地址:https://www.cnblogs.com/Colythme/p/10193202.html
Copyright © 2020-2023  润新知