• 【bzoj2588/P2633】count on a tree —— LCA + 主席树


    (以下是luogu题面)

    题目描述

    给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。

    输入输出格式

    输入格式:

    第一行两个整数N,M。

    第二行有N个整数,其中第i个整数表示点i的权值。

    后面N-1行每行两个整数(x,y),表示点x到点y有一条边。

    最后M行每行两个整数(u,v,k),表示一组询问。

    输出格式:

    M行,表示每个询问的答案。

    说明

    HINT:

    N,M<=100000

    思路:

      一看到静态第K小,你就要想到主席树;一看到树,你就要树链剖分

      的确用树剖把树搞成区间,在上面架主席树是比较直接的想法。问题是树剖把链所划分成的区间的数目是不确定的,因此树剖能维护的一般是能够对每条链独立求出,再用结合律结合得出答案的信息。但是主席树需要用确定多的几棵前缀权值树维护一个虚拟的树,查询函数每次传入的参数最好是一样多的。我猜树剖可以写,但是实在太麻烦,而且还多一个log。

      我们需要维护的其实只是两点之间简单路径的信息,联想到用树上差分实现树链修改的过程,我们可以用主席树维护一个类似于前缀的东西:定义root[i]表示从原树根节点到点i的路径上的权值线段树的根。每棵新树基于的原始版本是它父亲u的那棵树。这个思想与区间前缀和的关系就好比字符串之于Trie树(可能这么比喻也不恰当)。按dfs序建立这样的主席树之后,我们查询的树上路径可以这样求出:

      设S[l, r]维护从l到r路径的值域信息的线段树,则

        S[u, v] = S[root, u] + S[root, v] - S[root, lca(u, v)] - S[root, father(lca(u, v))]

      可以看到这个形式与树上节点信息差分很像。那么我们还需要维护LCA的查询。(终于可以用树剖辣!@w@……不,你不想)

      (倍增大法好

    1. #include <cstdio>  
    2. #include <iostream>  
    3. #include <cstring>  
    4. #include <algorithm>  
    5. #include <cctype>  
    6. #define BUG puts("$$$")  
    7. #define LG 17  
    8. #define maxn 100010  
    9. template <class T>   
    10. void read(T &x) {  
    11.     x = 0;  
    12.     char ch = getchar();  
    13.     while (!isdigit(ch))   
    14.         ch = getchar();  
    15.     while (isdigit(ch)) {  
    16.         x = x * 10 + (ch ^ 48);  
    17.         ch = getchar();  
    18.     }  
    19. }  
    20. using namespace std;  
    21. struct E {  
    22.     int to, nxt;  
    23. } edge[maxn << 1];  
    24. int head[maxn], top;  
    25. int n, m;  
    26. int a[maxn], N, st[maxn];  
    27. inline void insert(int u, int v) {  
    28.     edge[++top] = (E) {v, head[u]};  
    29.     head[u] = top;  
    30. }  
    31. namespace LCA {  
    32.     int f[LG + 2][maxn], d[maxn];  
    33.     void dfs(int u, int pre) {  
    34.         d[u] = d[pre] + 1;  
    35.         f[0][u] = pre;  
    36.         for (int i = head[u]; i; i = edge[i].nxt) {  
    37.             int v = edge[i].to;  
    38.             if (v != pre)  
    39.                 dfs(v, u);  
    40.         }  
    41.     }  
    42.     void init1() {  
    43.         dfs(1, 0);  
    44.         for (int k = 1; k <= LG; ++k)   
    45.             for (int i = 1; i <= n; ++i)  
    46.                 f[k][i] = f[k-1][f[k-1][i]];  
    47.     }  
    48.     void swim(int &x, int d) {  
    49.         for (int i = 0; d; d >>= 1, ++i)  
    50.             if (d & 1)  
    51.                 x = f[i][x];  
    52.     }  
    53.     int find(int u, int v) {  
    54.         if (d[u] > d[v]) swap(u, v);  
    55.         swim(v, d[v] - d[u]);  
    56.         if (u == v)  
    57.             return u;  
    58.         for (int k = LG; k >= 0; --k)  
    59.             if (f[k][u] != f[k][v])  
    60.                 u = f[k][u], v = f[k][v];  
    61.         return f[0][u];  
    62.     }  
    63. using namespace LCA;  
    64. namespace President_tree {  
    65.     #define lc(i) seg[i].lc  
    66.     #define rc(i) seg[i].rc  
    67.     #define mid ((l + r) >> 1)  
    68.     int root[maxn], tot;  
    69.     struct node {  
    70.         int cnt, lc, rc;  
    71.     } seg[maxn * 30];  
    72.     inline void update(int nd) {  
    73.         seg[nd].cnt = seg[lc(nd)].cnt + seg[rc(nd)].cnt;  
    74.     }  
    75.     int build(int l, int r) {  
    76.         int nd = ++tot;  
    77.         seg[nd].cnt = 0;  
    78.         if (l == r)  
    79.             return nd;  
    80.         lc(nd) = build(l, mid);  
    81.         rc(nd) = build(mid + 1, r);  
    82.         return nd;  
    83.     }  
    84.     int modify(int pre, int l, int r, int x) {  
    85.         int nd = ++tot;  
    86.         seg[nd] = seg[pre];  
    87.         if (l == r) {  
    88.             ++seg[nd].cnt;  
    89.             return nd;  
    90.         }  
    91.         if (x <= mid)  
    92.             lc(nd) = modify(lc(pre), l, mid, x);  
    93.         else rc(nd) = modify(rc(pre), mid + 1, r, x);  
    94.         update(nd);  
    95.         return nd;  
    96.     }  
    97.     void init2(int u, int pre) {  
    98.         root[u] = modify(root[pre], 1, N, a[u]);  
    99.         for (int i = head[u]; i; i = edge[i].nxt) {  
    100.             int v = edge[i].to;  
    101.             if (v != pre)  
    102.                 init2(v, u);  
    103.         }  
    104.     }  
    105.     int query(int u, int v, int lca, int flca, int l, int r, int k) {  
    106.         if (l == r) {  
    107.             return st[l];  
    108.         }  
    109.         int lsum = seg[lc(u)].cnt + seg[lc(v)].cnt - seg[lc(lca)].cnt - seg[lc(flca)].cnt;  
    110.         if (k <= lsum)  
    111.             return query(lc(u), lc(v), lc(lca), lc(flca), l, mid, k);  
    112.         return query(rc(u), rc(v), rc(lca), rc(flca), mid + 1, r, k - lsum);  
    113.     }  
    114. using namespace President_tree;  
    115. int contra(int* a) {  
    116.     for (int i = 1; i <= n; ++i)   
    117.         st[i] = a[i];  
    118.     sort(st + 1, st + 1 + n);  
    119.     int len = unique(st + 1, st + 1 + n) - st - 1;  
    120.     for (int i = 1; i <= n; ++i)  
    121.         a[i] = lower_bound(st + 1, st + len + 1, a[i]) - st;  
    122.     return len;  
    123. }  
    124. int main() {  
    125.     read(n), read(m);  
    126.     int u, v;  
    127.     for (int i = 1; i <= n; ++i)  
    128.         read(a[i]);  
    129.     N = contra(a);  
    130.     for (int i = 1; i < n; ++i) {  
    131.         read(u), read(v);  
    132.         insert(u, v), insert(v, u);  
    133.     }  
    134.     init1();  
    135.     init2(1, 0);  
    136.     int k, ans = 0;  
    137.     for (int i = 1; i <= m; ++i) {  
    138.         read(u), read(v), read(k);  
    139.         u = ans xor u;  
    140.         int lca = find(u, v);  
    141.         ans = query(root[u], root[v], root[lca], root[f[0][lca]], 1, N, k);  
    142.         printf("%d ", ans);  
    143.     }  
    144.     return 0;  
    145. }  
  • 相关阅读:
    游戏中的有限状态机 (StateMachine)
    关于在Cocos下面配置开发环境、编译移植到android上面的奇葩问题小结
    As3.0中的Dictionary使用测试
    Nginx(一)--nginx的初步认识及配置
    布隆过滤器的demo
    redis(7)--redis应用实战
    redis(6)--redis集群之分片机制(redis-cluster)
    redis(5)--redis集群之哨兵机制
    redis(4)--redis集群之主从复制
    redis(3)--redis原理分析
  • 原文地址:https://www.cnblogs.com/TY02/p/11190821.html
Copyright © 2020-2023  润新知