划分树 静态第k大

划分树是保存了快速排序的过程的树,可以用来求静态第k小的数

如果,划分树可以看做是线段树,它的左孩子保存了mid-L+1 个 小于等于 a[mid] 的数字,  右孩子保存了 R-mid个大于等于a[mid]的数字    

数组a是排序过后的数组,而划分树保存的是原数组的数据,

 

划分树的构造就是将上一层[l,r]个数的 mid-l+1个数划分到左子区间,r-(mid-l+1)个数划分到了右子区间

void build(int l, int r, int rt)
{
    if (l == r)
        return;
    int mid = (l + r) >> 1, isSame = mid - l + 1;
    for (int i = l; i <= r; ++i)
    if (tree[rt][i] < sortA[mid])//mid-l+1个数被划入了左子树,减去比sortA[mid]小的,那么就是记录几个和其相等的被划入左子树
        isSame--;
    int lPos = l, rPos = mid + 1;
    for (int i = l; i <= r; ++i)
    {
        //sum[rt][i] 为前i个数字,有多少个数字被划分到了左区间
        i == l ? sum[rt][i] = 0 : sum[rt][i] = sum[rt][i - 1];
        if (tree[rt][i] < sortA[mid])
        {
            sum[rt][i]++;
            tree[rt+1][lPos++] = tree[rt][i];
        }
        else if (tree[rt][i]>sortA[mid])
        {
            tree[rt + 1][rPos++] = tree[rt][i];
        }
        else
        {
            if (isSame > 0)
            {
                isSame--;
                sum[rt][i]++;
                tree[rt + 1][lPos++] = tree[rt][i];
            }
            else
            {
                tree[rt + 1][rPos++] = tree[rt][i];
            }
        }
    }
    build(l, mid, rt + 1);
    build(mid + 1, r, rt + 1);
}

那么查询区间[l,r]第k大, 就是如果 sum[rt][r] - sum[rt][l-1] 如果>=k, 说明该区间有sum[rt][r] - sum[rt][l-1] 个数被划分到了左子区间,

所以应该去左区间找第k小的数, 否则,去右子区间找第k -( sum[rt][r] - sum[rt][l-1] ) 大的数

int query(int l, int r, int rt, int L, int R, int k)
{
    int mid = (l + r) >> 1;
    if (l == r)
        return tree[rt][l];
    int s1, s2;
    if (l == L)
    {
        s1 = 0;
        s2 = sum[rt][R];
    }
    else
    {
        s1 = sum[rt][L - 1];
        s2 = sum[rt][R] - s1;
    }
    //要记住查询的区间,同样也变化了
    if (k<=s2)
        return query(l, mid, rt + 1, l + s1, l + s1 + s2 - 1, k);
    else
        return query(mid + 1, r, rt + 1, mid  + L  - l + 1 - s1, mid - l + 1 - s1 + R  - s2 ,k-s2);
}

 

 

poj2104

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <math.h>
using namespace std;
#pragma warning(disable:4996)
#pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;                   
const int INF = 1<<30;
/*

*/
const int N = 100000 + 10;
int a[N], sortA[N], tree[20][N],sum[20][N];

void build(int l, int r, int rt)
{
    if (l == r)
        return;
    int mid = (l + r) >> 1, isSame = mid - l + 1;
    for (int i = l; i <= r; ++i)
    if (tree[rt][i] < sortA[mid])//mid-l+1个数被划入了左子树,减去比sortA[mid]小的,那么就是记录几个和其相等的被划入左子树
        isSame--;
    int lPos = l, rPos = mid + 1;
    for (int i = l; i <= r; ++i)
    {
        //sum[rt][i] 为前i个数字,有多少个数字被划分到了左区间
        i == l ? sum[rt][i] = 0 : sum[rt][i] = sum[rt][i - 1];
        if (tree[rt][i] < sortA[mid])
        {
            sum[rt][i]++;
            tree[rt+1][lPos++] = tree[rt][i];
        }
        else if (tree[rt][i]>sortA[mid])
        {
            tree[rt + 1][rPos++] = tree[rt][i];
        }
        else
        {
            if (isSame > 0)
            {
                isSame--;
                sum[rt][i]++;
                tree[rt + 1][lPos++] = tree[rt][i];
            }
            else
            {
                tree[rt + 1][rPos++] = tree[rt][i];
            }
        }
    }
    build(l, mid, rt + 1);
    build(mid + 1, r, rt + 1);
}

int query(int l, int r, int rt, int L, int R, int k)
{
    int mid = (l + r) >> 1;
    if (l == r)
        return tree[rt][l];
    int s1, s2;
    if (l == L)
    {
        s1 = 0;
        s2 = sum[rt][R];
    }
    else
    {
        s1 = sum[rt][L - 1];
        s2 = sum[rt][R] - s1;
    }
    //要记住查询的区间,同样也变化了
    if (k<=s2)
        return query(l, mid, rt + 1, l + s1, l + s1 + s2 - 1, k);
    else
        return query(mid + 1, r, rt + 1, mid  + L  - l + 1 - s1, mid - l + 1 - s1 + R  - s2 ,k-s2);
}
int main()
{
    int n, m, k, L, R;
    int t;
    
    while (scanf("%d%d", &n, &m)!=EOF)
    {
        
        for (int i = 1; i <= n; ++i)
        {
            scanf("%d", &a[i]);
            tree[0][i] = sortA[i] = a[i];
        }
        sort(sortA, sortA + n + 1);
        build(1, n, 0);
        while (m--)
        {
            scanf("%d%d%d", &L, &R, &k);
            printf("%d\n", query(1, n, 0, L, R, k));
        }
    }
    return 0;
}

 

posted @ 2015-07-15 11:22  justPassBy  阅读(282)  评论(0编辑  收藏  举报