洛谷2633 Count on a tree 题解(主席树)

题目大意

给定一个\(n\)个点的数,有\(m\)个询问,每次寻味两点之间最短距离上第\(k\)小的点权

思路

树上主席树。

学习了序列上的主席树之后,会发现主席树其实是一种前缀操作(在每个结点都建立一颗线段树)。因此这题我们也可以这样做,我们定义\(T[u]\)表示从根节点到到第\(u\)号节点的前缀主席树

所以包含\(u\)\(v\)上的所有信息的就是:\(T[u]+T[v]-T[lca]-T[fa[lca]]\)(前缀和与差分是逆操作,所以可以用差分还原)

那么明白了这些后,代码就很显然了

#include <bits/stdc++.h>
using namespace std ;
const int MAXN = 1e5 + 5 ;
int n , m , tot , L ;
int a[ MAXN ] , b[ MAXN ] ;
int dep[ MAXN ] , fa[ MAXN ][ 20 ] ;
struct Node {
    int next , to ;
} edge[ MAXN << 1 ] ;
int head[ MAXN ] , cnt ;
struct Tree {
    int l , r , sum ;
} T[ MAXN << 5 ] ;
int rt[ MAXN ] ;
inline int read () {
    int tot = 0 , f = 1 ; char c = getchar () ;
    while ( c < '0' || c > '9' ) { if ( c == '-' ) f = -1 ; c = getchar () ; }
    while ( c >= '0' && c <= '9' ) { tot = tot * 10 + c - '0' ; c = getchar () ; }
    return tot * f ;
}
inline int id ( int x ) { return lower_bound ( b + 1 , b + 1 + L , a[ x ] ) - b ; } 
inline void add ( int x , int y ) {
    edge[ ++ cnt ].next = head[ x ] ;
    edge[ cnt ].to = y ;
    head[ x ] = cnt ;
}
inline void build ( Tree &u , int l , int r ) {
    u.sum = 0 ;
    if ( l == r ) return ;
    int mid = ( l + r ) >> 1 ;
    build ( T[ u.l = ++ tot ] , l , mid ) ;
    build ( T[ u.r = ++ tot ] , mid + 1 , r ) ;
}
inline void modify ( Tree &u , Tree ff , int l , int r , int p ) {
    u.sum = ff.sum + 1 ;
    if ( l == r ) return ;
    int mid = ( l + r ) >> 1 ;
    if ( p <= mid ) modify ( T[ u.l = ++ tot ] , T[ ff.l ] , l , mid , p ) , u.r = ff.r ;
    else modify ( T[ u.r = ++ tot ] , T[ ff.r ] , mid + 1 , r , p ) , u.l = ff.l ;
}
inline int query ( Tree x , Tree y , Tree z , Tree f , int l , int r , int k ) {
    if ( l == r ) return l ;
    int res = T[ x.l ].sum + T[ y.l ].sum - T[ z.l ].sum - T[ f.l ].sum ;
    int mid = ( l + r ) >> 1 ;
    if ( res >= k ) return query ( T[ x.l ] , T[ y.l ] , T[ z.l ] , T[ f.l ] , l , mid , k ) ;
    else return query ( T[ x.r ] , T[ y.r ] , T[ z.r ] , T[ f.r ] , mid + 1 , r , k - res ) ; // warning!!
}
// 以上为主席树基本操作
inline void dfs ( int u , int father ) {
    modify ( T[ rt[ u ] = ++ tot ] , T[ rt[ father ] ] , 1 , L , id ( u ) ) ; //建主席树
    dep[ u ] = dep[ father ] + 1 ;
    fa[ u ][ 0 ] = father ;
    for ( int i = 1 ; i <= 18 ; i ++ )
        fa[ u ][ i ] = fa[ fa[ u ][ i - 1 ] ][ i - 1 ] ;
    for ( int i = head[ u ] ; i ; i = edge[ i ].next ) {
        int v = edge[ i ].to ;
        if ( v == father ) continue ;
        dfs ( v , u ) ;
    }
}
inline int Lca ( int x , int y ) {
    if ( dep[ x ] < dep[ y ] ) swap ( x , y ) ;
    if ( x == y ) return x ;
    for ( int i = 18 ; i >= 0 ; i -- ) {
        if ( dep[ fa[ x ][ i ] ] >= dep[ y ] ) x = fa[ x ][ i ] ;
    }
    if ( x == y ) return x ;
    for ( int i = 18 ; i >= 0 ; i -- ) {
        if ( fa[ x ][ i ] != fa[ y ][ i ] ) x = fa[ x ][ i ] , y = fa[ y ][ i ] ;
    }
    return fa[ x ][ 0 ] ;
}
//倍增LCA的基本操作
signed main () {
    n = read () ; m = read () ;
    for ( int i = 1 ; i <= n ; i ++ ) b[ i ] = a[ i ] = read () ;
    for ( int i = 1 ; i < n ; i ++ ) {
        int x = read () , y = read () ;
        add ( x , y ) ; add ( y , x ) ;
    }
    sort ( b + 1 , b + 1 + n ) ;
    L = unique ( b + 1 , b + 1 + n ) - b - 1 ;//离散化
    build ( T[ rt[ 0 ] = ++ tot ] , 1 , L ) ;
    dfs ( 1 , 0 ) ;
    int ans = 0 ;
    while ( m -- ) {
        int u = read () , v = read () , w = read () ;
        u ^= ans ;
        int lca = Lca ( u , v ) ;
        // cout << u << " " << v << " " << lca << endl ;
        ans = query ( T[ rt[ u ] ] , T[ rt[ v ] ] , T[ rt[ lca ] ] , T[ rt[ fa[ lca ][ 0 ] ] ] , 1 , L , w ) ;
        ans = b[ ans ] ;
        printf ( "%d\n" , ans ) ;
    }
    return 0 ;
}

posted @ 2020-08-20 22:05  hulean  阅读(173)  评论(0编辑  收藏  举报