zoukankan      html  css  js  c++  java
  • 划分树 静态第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
    ", query(1, n, 0, L, R, k));
            }
        }
        return 0;
    }
  • 相关阅读:
    Jquery 添加插件
    后台添加前台标签
    jQuery.validate 中文API
    jquery validate 详解二
    jquery validate 详解一
    System.Collections里的一些接口
    C#中 Reference Equals, == , Equals的区别
    关于iOS原生条形码扫描,你需要注意的两三事
    layoutSubviews何时调用的问题(转)
    layoutSubviews总结
  • 原文地址:https://www.cnblogs.com/justPassBy/p/4643976.html
Copyright © 2011-2022 走看看