poj 2104 K-th Number (划分树)

You are working for Macrohard company in data structures department. After failing your previous task about key insertion you were asked to write a new data structure that would be able to return quickly k-th order statistics in the array segment. 
That is, given an array a[1...n] of different integer numbers, your program must answer a series of questions Q(i, j, k) in the form: "What would be the k-th number in a[i...j] segment, if this segment was sorted?" 
For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3). The segment a[2...5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6), the third number is 5, and therefore the answer to the question is 5.

Input

The first line of the input file contains n --- the size of the array, and m --- the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000). 
The second line contains n different integer numbers not exceeding 10 9 by their absolute values --- the array for which the answers should be given. 
The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).

Output

For each question output the answer to it --- the k-th number in sorted a[i...j] segment.

Sample Input

7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3

Sample Output

5
6
3

Hint

This problem has huge input,so please use c-style input(scanf,printf),or you may got time limit exceed.

学完主席树之后再看划分树,划分树代码较短,但是理解起来还是挺费劲的,建树比较简单就是查询比较难,有许多细节。

本以为划分树能处理的问题主席树一般都能解决,但是竟然发现了卡主席树(卡内存)的题目,就还是学习了一下。

找了个模板题学习了一下,记录下来,以便以后查看。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100005

int t[20][N];
int cnt[20][N];
int sor[N];

void build(int l,int r,int dep)//dep为0时储存的是原序列。
{
    if(l==r)return ;
    int m=(l+r)>>1;
    int same = m-l+1;
    for(int i=l;i<=r;i++)if(t[dep][i]<sor[m])same--;//尽量使划分的区间个数相等。
    int ln=l;
    int rn=m+1;
    for(int i=l;i<=r;i++)
    {
        if(t[dep][i]<sor[m]||(t[dep][i]==sor[m]&&same>0))
        {
            t[dep+1][ln++]=t[dep][i];
            if(t[dep][i]==sor[m])same--;
            cnt[dep][i]=cnt[dep][l-1]+ln-l;
        }
        else
        {
            t[dep+1][rn++]=t[dep][i];
            cnt[dep][i]=cnt[dep][i-1];
        }
    }
    build(l,m,dep+1);
    build(m+1,r,dep+1);
}

int query(int l,int r,int ql,int qr,int k,int dep)
{
    if(l==r)return t[dep][l];
    int m = (l+r)>>1;
    int ss = cnt[dep][qr]-cnt[dep][ql-1];//查询的区间内到左子区间的个数。
    if(ss>=k)
    {
        int newl = l+cnt[dep][ql-1]-cnt[dep][l-1];
        int newr = newl+ss-1; //减一是为了处理边界问题(具体还不理解)
        return query(l,m,newl,newr,k,dep+1);
    }
    else
    {
        int newr = qr+cnt[dep][r]-cnt[dep][qr];//这里qr向后推了cnt[dep][r]-cnt[dep][qr]
        int newl = newr-(qr-ql-ss);//知道qr后用查询区间到左子区间的个数推出newl
        return query(m+1,r,newl,newr,k-ss,dep+1);
    }
}
int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&t[0][i]);
            sor[i]=t[0][i];
        }
        sort(sor+1,sor+1+n);
        build(1,n,0);
        while(m--)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",query(1,n,l,r,k,0));
        }
    }
}

其实仔细想想,查询的过程其实也挺简单的,就是利用了划分树不改变原序列相对顺序的性质,一步一步递归查询。

猜你喜欢

转载自blog.csdn.net/weixin_40894017/article/details/81699867