SPOJ COT (主席树 LCA)

题目链接:点击打开链接

题意:求树上u-v之间路径上的第k小的节点值.

和数列的第k小类似不过写起来烦一点.在数列上求区间第k小是两个前缀减一减,

树上就是u-root,v-root前缀加一加减去两倍LCA-root,新建线段树都是在父亲节

的基础上更新的.

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <queue>
using namespace std;
#define maxn 211111
#define maxm maxn*40

int a[maxn], n, m, q;
vector <int> num;
map <int , int> gg;
struct E {
    int v, next;
}edge[maxn];
int head[maxn], cnt, Hash[maxn];

//主席树
struct node {
    int l, r, num;
}tree[maxm];
int tot, T[maxn];

int build_tree (int l, int r) {
    int root = tot++;
    tree[root].num = 0;
    if (l == r)
        return root;
    int mid = (l+r)>>1;
    tree[root].l = build_tree (l, mid);
    tree[root].r = build_tree (mid+1, r);
    return root;
}

int update (int root, int pos, int val) {
    int new_root = tot++;
    int tmp = new_root;
    tree[new_root].num = tree[root].num+val;
    int l = 1, r = m;
    while (l < r) {
        int mid = (l+r)>>1;
        if (pos <= mid) {
            tree[new_root].r = tree[root].r;
            tree[new_root].l = tot++;
            new_root = tree[new_root].l;
            root = tree[root].l;
            r = mid;
        }
        else {
            tree[new_root].l = tree[root].l;
            tree[new_root].r = tot++;
            new_root = tree[new_root].r;
            root = tree[root].r;
            l = mid+1;
        }
        tree[new_root].num = tree[root].num + val;
    }
    return tmp;
}

void dfs (int u, int fa) {
    T[u] = update (T[fa], a[u], 1);
    for (int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].v;
        if (v == fa) continue;
        dfs (v, u);
    }
}

void build () {
    tot = 0;
    T[n+1] = build_tree (1, m);
    dfs (1, 0);
}

int query (int l_root, int r_root, int root, int k) {//u,v,lca的主席树节点
    int pos = a[root];
    root = T[root];
    int l = 1, r = m;
    while (l < r) {
        int mid = (l+r)>>1;
        int tmp = tree[tree[l_root].l].num + tree[tree[r_root].l].num -
        tree[tree[root].l].num*2 + (pos >= l && pos <= mid);
        if (tmp >= k) {
            l_root = tree[l_root].l;
            r_root = tree[r_root].l;
            root = tree[root].l;
            r = mid;
        }
        else {
            k -= tmp;
            l_root = tree[l_root].r;
            r_root = tree[r_root].r;
            root = tree[root].r;
            l = mid+1;
        }
    }
    return l;
}

//LCA
int fa[maxn][22], deep[maxn];//节点i第2^j个祖先 深度
int DEG;
bool vis[maxn];

void bfs (int root) {
    DEG = 20;
    queue <int> q;
    while (!q.empty ()) q.pop ();
    deep[root] = 0;
    fa[root][0] = root;
    q.push (root);
    memset (vis, 0, sizeof vis); vis[root] = 1;
    while (!q.empty ()) {
        int u = q.front (); q.pop ();
        for (int i = 1; i < DEG; i++) {
            fa[u][i] = fa[fa[u][i-1]][i-1];
        }
        for (int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].v;
            if (vis[v]) continue;
            deep[v] = deep[u]+1;
            fa[v][0] = u;
            q.push (v);
            vis[v] = 1;
        }
    }
}

int LCA (int u, int v) {
    if (deep[u] > deep[v])
        swap (u, v);
    int hu = deep[u], hv = deep[v];
    int tu = u, tv = v;
    for (int det = hv-hu, i = 0; det; det >>= 1, i++) if (det&1) {
        tv = fa[tv][i];
    }
    if (tu == tv)
        return tu;
    for (int i = DEG-1; i >= 0; i--) {
        if (fa[tu][i] == fa[tv][i])
            continue;
        tu = fa[tu][i];
        tv = fa[tv][i];
    }
    return fa[tu][0];
}

void add_edge (int u, int v) {
    edge[cnt].v = v, edge[cnt].next = head[u], head[u] = cnt++;
}

void init () {//初始化
    gg.clear ();
    num.clear ();
    memset (head, -1, sizeof head);
    cnt = 0;
}

void lisan () {//离散化
    int cnt = 0;
    sort (num.begin (), num.end ());
    for (int i = 0; i < n; i++) {
        if (!i || num[i] != num[i-1]) {
            gg[num[i]] = ++cnt;
            Hash[cnt] = num[i];
        }
    }
    for (int i = 1; i <= n; i++) a[i] = gg[a[i]];
    m = cnt;
}

int main () {
    while (scanf ("%d%d", &n, &q) == 2) {
        init ();
        for (int i = 1; i <= n; i++) {
            scanf ("%d", &a[i]);
            num.push_back (a[i]);
        }
        lisan ();
        for (int i = 1; i < n; i++) {
            int u, v;
            scanf ("%d%d", &u, &v);
            add_edge (u, v);
            add_edge (v, u);
        }
        bfs (1);
        build ();
        for (int i = 1; i <= q; i++) {
            int u, v, k;
            scanf ("%d%d%d", &u, &v, &k);
            int fa = LCA (u, v);
            printf ("%d\n", Hash[query (T[u], T[v], fa, k)]);
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值