zoukankan      html  css  js  c++  java
  • 洛谷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
    " , ans ) ;
        }
        return 0 ;
    }
    
    
  • 相关阅读:
    修改Firebug字体
    [CodeWars][JS]如何判断给定的数字是否整数
    [CodeWars][JS]实现链式加法
    【ACM成长之路】刷题记录
    【C++】用于ACM/OI等算法竞赛的读入优化
    C# 读取写入excel单元格(包括对excel的一些基本操作)
    Git上传本地项目到GitHub等云托管仓库
    贝塞尔曲线(B-spline)的原理与应用
    【已解决】Ubuntu U盘启动出现“Failed to load ldlinux.c32”问题
    【算法】Tarjan算法求强连通分量
  • 原文地址:https://www.cnblogs.com/hulean/p/13538146.html
Copyright © 2011-2022 走看看