BZOJ 2588 Count on a tree(树上的主席树)

5 篇文章 0 订阅
1 篇文章 0 订阅

2588: Spoj 10628. Count on a tree

Time Limit: 12 Sec  Memory Limit:128 MB
Submit: 7489  Solved: 1824
[Submit][Status][Discuss]

Description

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

Input

第一行两个整数N,M。
第二行有N个整数,其中第i个整数表示点i的权值。
后面N-1行每行两个整数(x,y),表示点x到点y有一条边。
最后M行每行两个整数(u,v,k),表示一组询问。

Output

M行,表示每个询问的答案。最后一个询问不输出换行符

Sample Input

8 5
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5 1
0 5 2
10 5 3
11 5 4
110 8 2

Sample Output

2
8
9
105

7


强制在线的话就只能主席树了。

学的主席树只能处理序列, 怎么办呢?

其实dfs序也是个序列2333, 于是我们就可以对dfs序建主席树了

只不过root[i] 不是指向 root[i-1], 而是指向了dfn[fa[re[i]]], i 是当前点的dfs序,re[i]是当前点, 前缀和也不是序列前缀和了, 是树上前缀和

#include <cstdio>
#include <algorithm>
#define lson l, m, lch[now]
#define rson m+1, r, rch[now]
#define N 100005
using namespace std;
struct node{
    int a, b, c, n;
}d[N * 2];
int lch[N * 20], rch[N * 20], rt[N * 20], sum[N * 20], dfn[N], h[N], re[N];
int siz[N], top[N], son[N], fa[N], dep[N], w[N], tot, cnt, z, p, sz;
int pai[N], v[N];
void cr(int a, int b){
    d[++z].a = a; d[z].b = b; d[z].n = h[a]; h[a] = z;
}
void dfs1(int a){
    int i, b;
    siz[a] = 1;
    dfn[a] = ++cnt;
    re[cnt] = a;
    for(i = h[a]; i; i = d[i].n){
        b = d[i].b;
        if(b != fa[a]){
            dep[b] = dep[a] + 1;
            fa[b] = a;
            dfs1(b);
            if(siz[b] >= siz[son[a]]) son[a] = b;
            siz[a] += siz[b];
        }
    }
}
void dfs2(int a, int tp){
    int i, b;
    top[a] = tp;
    if(son[a]) dfs2(son[a], tp);
    for(i = h[a]; i; i = d[i].n){
        b = d[i].b;
        if(b != fa[a] && son[a] != b) dfs2(b, b);
    }
}
void update(int l, int r, int &now, int las, int pos){
    now = ++p;
    sum[now] = sum[las] + 1;
    if(l == r) return;
    lch[now] = lch[las];
    rch[now] = rch[las];
    int m = l+r>>1;
    if(pos <= m) update(lson, lch[las], pos);
    else update(rson, rch[las], pos);
}
int getlca(int a, int b){
    int f1 = top[a], f2 = top[b];
    while(f1 != f2){
        if(dep[f1] < dep[f2]) swap(f1, f2), swap(a, b);
        a = fa[f1], f1 = top[a];
    }
    return dep[a] < dep[b] ? a : b;
}
int query(int l, int r, int k){
    int a = l, b = r, c = getlca(a, b), d = fa[c], mid, cntt;
    a = rt[dfn[a]], b = rt[dfn[b]], c = rt[dfn[c]], d = rt[dfn[d]];
    l = 1, r = sz;
    while(l < r){
        mid = l+r>>1;
        cntt = sum[lch[a]] + sum[lch[b]] - sum[lch[c]] - sum[lch[d]];
        if(cntt >= k) r = mid, a = lch[a], b = lch[b], c = lch[c], d = lch[d];
        else l = mid+1, a = rch[a], b = rch[b], c = rch[c], d = rch[d], k -= cntt;
    }
    return v[l];
}
int main(){
    int i, j, n, m, lastans = 0, k, t, a, b, c;
    scanf("%d%d", &n, &m);
    for(i = 1; i <= n; i++) scanf("%d", &w[i]);
    for(i = 1; i < n; i++){
        scanf("%d%d", &a, &b);
        cr(a, b); cr(b, a);
    }
    dfs1(1); 
    dfs2(1, 1);
    for(i = 1; i <= n; i++) v[i] = pai[i] = w[i];
    sort(v + 1, v + i);
    sz = unique(v + 1, v + 1 + n) - (v + 1);
    for(i = 1; i <= n; i++) pai[i] = lower_bound(v + 1, v + 1 + sz, w[i]) - v;
    for(i = 1; i <= n; i++){
        t = re[i];
        update(1, sz, rt[i], rt[dfn[fa[t]]], pai[t]);
    }
    while(m--){
        scanf("%d%d%d", &a, &b, &k);
        a ^= lastans;
        lastans = query(a, b, k);
        if(m >= 1) printf("%d\n", lastans);
        else printf("%d", lastans);
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值