poj 2104 K-th Number (主席树学习第一弹)

题目链接:

poj 2104

题目大意:

给出n个数,询问m次,每次询问在区间[l,r]里的第k大的树。

题目分析:

  • 其实是可以直接划分树搞的,但是为了学习主席树,写了一发,发现代码量更少更简洁。
  • 首先将数字离散化,然后对数字进行建树,然后按照原顺序对主席树进行修改,也就是将数位对应到时序上,那么也就是每次将查询对应到就是查找时序在找到r时序的线段树和l时序的线段树的差,每次向下深入时,就是如果左子树的数的数目大于等于k,那么就递归到左子树,否则,就递归到右子树,找到长度为1的区间时就是最终答案。
  • 一个比较简单的主席树的离线处理

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAX 100007

using namespace std;

struct Tree
{
    int l,r,sum;
}tree[3000007];

int cnt = 0;

void build ( int &u , int l , int r )
{
    u = ++cnt;
    tree[u].sum = 0;
    if ( l == r ) return;
    int mid = l+r>>1;
    build ( tree[u].l , l , mid );
    build ( tree[u].r , mid+1 , r );
}

void update ( int p , int &u , int l , int r , int x )
{
    u = ++cnt;
    tree[u] = tree[p];
    tree[u].sum++;
    int mid = l+r>>1;
    if ( l == r ) return;
    if ( x > mid )
        update ( tree[p].r , tree[u].r , mid+1 , r , x );
    else 
        update ( tree[p].l , tree[u].l , l , mid  , x );
}

int query ( int t1 , int t2 , int l , int r , int k )
{
    if ( l == r ) return l;
    int mid = l+r>>1;
    int left = tree[tree[t2].l].sum - tree[tree[t1].l].sum;
    if ( left >= k )
        return query ( tree[t1].l , tree[t2].l , l , mid , k );
    else 
        return query ( tree[t1].r , tree[t2].r , mid+1 , r , k-left );
}

int a[MAX],num[MAX],num_cnt,root[MAX],n,m;

int main ( )
{
    while ( ~scanf ( "%d%d" , &n , &m ) )
    {
        cnt = 0;
        for ( int i = 1 ; i <= n ; i++ )
        {
            scanf ( "%d" , &a[i] );
            num[i] = a[i];
        }
        sort ( num+1 , num+n+1 );
        num_cnt = unique ( num+1 , num+n+1 )-num-1;
        build ( root[0] , 1 , num_cnt );
        for ( int i = 1 ; i <= n ; i++ )
        {
            int x = lower_bound ( num+1 , num+n+1 , a[i] )-num;
            update ( root[i-1] , root[i] , 1 , num_cnt , x );
        }
        for ( int i = 1 ; i <= m ;i++ )
        {
            int l,r,k;
            scanf ( "%d%d%d" , &l , &r , &k );
            int x = query ( root[l-1] , root[r] , 1 , num_cnt , k );
            printf ( "%d\n" , num[x] );
        }
    }
    return 0;
}

你可能感兴趣的:(主席树,可持续化线段树,区间第k大)