函数式 - 主席树 > <

    · 博主语文体育老师教的

    · 本文年龄限定 16+

    · 吐槽上面 2条的都是⑨



    很久以前的坑了……

    因为最近刷到树上路径K大值什么的, 实在不想写XX分块树......


    推荐文章 :

        1、丽洁姐的《可持久化数据结构研究》

        2、Seter 对 BZOJ 1901 的吐槽 - http://seter.is-programmer.com/posts/31907.html ( 仅用来剽垃圾回收代码而已......)


    具体就是考虑一个单点修改的线段树, 要求询问第 K 次修改时的线段树形态.

    如果对每次修改都新开一个O(nlogn) 的线段树,    很欠抽不是么> <~

    所以考虑是 " 单点修改 ", 每次修改都是O(logn) 的,   即只会影响 O(logn) 个节点.

    那么对于第 K 次修改,  只对需要修改的节点新开一个版本, 其余不需要修改的节点直接调用 K - 1次时的状态就可以了.

    比如说对于节点 P,  修改了Right [P],   就 Right [P] = New ( Right [P] ),  Left [P] = Left[P]

    也许会吐槽说 “ 那这样原来的版本不也直接被覆盖了么魂淡! ” , 但考虑到修改了 Right [P], P也会被修改, 也就是说 New (P) 也是会执行的.

    最后追溯到 New (Tree_Root), 只需要 Root [K] = New(Tree_Root) 就可以了.  这样询问第K次修改与询问第K - 1修改会从完全不同的 2 个树根Root [K], Rook [k - 1]开始查找.  

    这样相当于空间消耗只有 O((n + q)logn) 其中 q 为修改次数.


    伪代码如下 :

    Function New (node p)

        if  P IS A LEAF                                                   // p 是叶子, 直接新建.

             return new(p);

        new(p);                                                              //对节点 p 新开一个版本, New 和 new 不一样哦~

        if  CHANGE RIGHT [P]                                

            Right [new (p)] = New (Right [p])              //修改右子树

        else

            Left [new (p)] = New (Left [p])                    //修改左子树

        return new (p)

    End Function   


    以下是树上两点路径 K 大值的 Code ...... LCA + 主席树, 

    考虑对于树上权值离散化,  按权值建一颗空线段树标记为 Root [0]   

    之后对于节点 P,   Root [P] = New (Root[father(P)])

    这样 Root [P] 就相当于将从根节点到 P 路径上的所有节点权值加入后的线段树形态.

    那么对于一个询问 A, B, K,   就是在线段树 Root[A] + Root[B] - Root[Lca(A, B)] - Root[father(Lca(A, B))] ( 这样可以得到将 A, B路径中所有节点权值加入后的线段树形态) 中找.

    其实想理解的话还是看看丽洁姐的论文比较好……人家是语文蒟蒻.


#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define mid ((l + r) >> 1)
#define gs (c < '0' || c > '9')
int getint() { int wis = 0; char c = getchar(); while (gs) c = getchar(); while (!gs) wis = wis * 10 + c - '0', c = getchar(); return wis; }
const int ys = 100010, max_i = 17;
using namespace std;

bool t[ys];
int n, m, x, y, k, sg, num, ufa, lastans;
int tot[ys * 30], left[ys * 30], right[ys * 30];
int f[ys][20], deep[ys], root[ys];
int v[ys], g[ys], p[ys]; 
int c[ys * 2], gv[ys], next[ys * 2];

int build(int l, int r)
{
    int p = ++sg;
    if (l == r)  return p;  
    left[p] = build(l, mid);
    right[p] = build(mid + 1, r);
    return p;
}
int change(int q, int w, int l, int r)
{
    if (l == r)  {  tot[++sg] = tot[w] + 1;  return sg; }
    int p = ++sg;  tot[p] = tot[w] + 1; 
    if (q <= mid)  left[p] = change(q, left[w], l, mid), right[p] = right[w];
    else  right[p] = change(q, right[w], mid + 1, r), left[p] = left[w];
    return p;    
}
void dfs(int z, int fa, int de)
{
    root[z] = change(p[z], root[fa], 1, n);  t[z] = true;  f[z][0] = fa;  deep[z] = de;
    for (int i = 0; f[z][i]; ++i)
      f[z][i + 1] = f[f[z][i]][i]; 
    for (int x = gv[z]; x; x = next[x])
      if (!t[c[x]])  dfs(c[x], z, de + 1);
}
int ask(int a, int b, int c, int d, int k)
{
    int l = 1, r = n;
    while (l != r)
      {
         if (tot[left[a]] + tot[left[b]] - tot[left[c]] - tot[left[d]] >= k)
           r = mid, a = left[a], b = left[b], c = left[c], d = left[d];
         else
           k -= tot[left[a]] + tot[left[b]] - tot[left[c]] - tot[left[d]], l = mid + 1,  
           a = right[a], b = right[b], c = right[c], d = right[d];
      }    
    return v[g[l]];
}
int lca(int x, int y)
{
    if (x == y)  return x;
    if (deep[x] < deep[y])  swap(x, y);
    for (int i = max_i; i >= 0; --i)
      if ((deep[x] - deep[y]) & (1 << i))
        x = f[x][i];
    if (x == y)  return y;
    for (int i = max_i; i >= 0; --i)
      if (f[x][i] != f[y][i])
        x = f[x][i], y = f[y][i];
    return f[x][0];
}
void add(int x, int y)
{
    c[++num] = y, next[num] = gv[x], gv[x] = num;
    c[++num] = x, next[num] = gv[y], gv[y] = num; 
}
bool cmp(const int &a, const int &b) {  return v[a] < v[b];  }
int main()
{
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    n = getint(); m = getint();
    for (int i = 1; i <= n; ++i)  v[i] = getint(), g[i] = i;
    sort(g + 1, g + n + 1, cmp);  root[0] = build(1, n); 
    for (int i = 1; i <= n; ++i)  p[g[i]] = i;
    for (int i = 1; i < n; ++i)  x = getint(), y = getint(), add(x, y);
    dfs(1, 0, 1);
    for (int i = 1; i <= m; ++i)
      {
      x = getint() ^ lastans, y = getint(), k = getint(), ufa = lca(x, y); 
      printf("%d", lastans = ask(root[x], root[y], root[ufa], root[f[ufa][0]], k));
      if (i != m)  printf("\n"); 
      }
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值