• 划分树


    原文

    传说中的划分树,只闻其名未见其身。然后搜索了一下划分树的资料,擦擦擦,这不就是同快排的原理+线段树的操作,两者一融合进化成了划分树么。前面两个都会,学习起来倍感轻松。

     建树过程: 先对区间[1,n]内所有元素进行排序,未排序之前的数列赋值给线段树的第一层元素(tree[0][i]),然后就是同快排的原理以排序后中间元素为参 照,小于它的放在树下一层的左边,大于它的放在树下一层的右边(划分树建树以中间元素为参照,快排以第一关键元素为参照)。然后再开一个sum数 组,sum[d][i]表示第d层前i个元素有多少个元素小于as[mid](as[mid]为排序后的中间值),这样一层一层建树下去最后建完树后等于 对原数列排好了序。

    查询过程: 这里最关键了。在查找区间[tl,tr]时,往下查询[tl,tr]左右孩子时,都要对区间[tl,tr]进行更新。

    定义两个数s, ss,  d表示第d层, k(k表示要查询的第k元素) :

    s 表示区间[l,tl]有多少个元素小于as[mid],  s=sum[d][tl-1];

    ss 表示区间[tl,tr]有多少个元素小于as[mid], ss=sum[d][tr]-s;

    if(ss>=k)  则下一层查询区间为[l+s,l+s+ss-1];

    else  则下一层查询区间为[mid+1+tl-l-s,mid+1+tr-l-s-ss];

    自己懒,不愿画图多解释,借用一下小媛姐姐的图。

    分析一下算法复杂度: 建树nlogn+查询mlogn,很强大的说。

    /******************************************************************
    *  HDU:4251
    *  给出一列数,求区间[l,r]内第k小
    ******************************************************************/
    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<iostream>
    #include<algorithm>
    #define INF 0x3f3f3f3f
    #define _Clr(x, y) memset(x, y, sizeof(x))
    using namespace std;
    
    typedef long long LL;
    const int N = 1e5+10;
    int arr[N], as[N]; // 原数组,排序后的数组
    int sum[20][N];     // sum[d][i]表示第d层前i个数有多少个小于as[mid]
    int tree[20][N];
    
    void build(int d, int l, int r)
    {
        int m = (l+r)>>1;
        int lp=l, rp=m+1, lm = m-l+1;
        for(int i=l; i<=m; i++)
            if(as[i]<as[m]) lm--; ///!!! 先假设前mid-l+1个数都等于as[mid],as[i]比它小则减1
        for(int i=l; i<=r; i++)
        {
            if(i==l) sum[d][i] = 0;
            else sum[d][i] = sum[d][i-1];
            if(tree[d][i]==as[m])
            {
                if(lm)
                {
                    lm--;
                    sum[d][i]++;
                    tree[d+1][lp++] = tree[d][i];
                }
                else
                    tree[d+1][rp++] = tree[d][i];
            }
            else if(tree[d][i]< as[m])
            {
                sum[d][i]++;
                tree[d+1][lp++] = tree[d][i];
            }
            else tree[d+1][rp++] = tree[d][i];
        }
        if(l==r) return;
        build(d+1, l, m);
        build(d+1, m+1, r);
    }
    
    int query_kth(int d, int l, int r, int L, int R, int k)
    {
        int s, ss, m=(l+r)>>1;
        if(l==r) return tree[d][l];
        if(l==L) s=0, ss=sum[d][R]; // 特判
        else s=sum[d][L-1], ss=sum[d][R]-s;
        if(ss>=k) return query_kth(d+1, l, m, l+s, l+s+ss-1, k);
        else return query_kth(d+1, m+1, r, m+1+L-l-s, m+1+R-l-s-ss, k-ss);
    }
    
    int main()
    {
    std::ios::sync_with_stdio(false);
    #ifndef DEBUG
        freopen("input", "r", stdin);
    #endif
        int n, m, a, b, lop=0;
        while(~scanf("%d", &n))
        {
            for(int i=1; i<=n; i++)
            {
                scanf("%d", arr+i);
                tree[1][i] = as[i] = arr[i];
            }
    
            sort(as+1, as+n+1);
            build(1, 1, n);
    
            scanf("%d", &m);
            printf("Case %d:
    ", ++lop);
            while(m--)
            {
                scanf("%d%d", &a, &b);
                int ans = query_kth(1, 1, n, a, b, (b-a)/2+1);
                printf("%d
    ", ans);
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    .NET日期格式化
    Win7 计算机(我的电脑)右键菜单“管理”打不开,解决方法
    没有对“Temporary ASP.NET Files”的写访问权限
    Android安装jsk出错
    WPF 处理 系统Scale参数
    WPF WindowChrome 自定义标题栏时窗体阴影效果设置
    使用WindowChrome 在切换ResizeMode值时的问题
    Vue.js provide / inject 踩坑
    MYSQL 查询日期最大的那条记录
    所有子一级元素添加阴影
  • 原文地址:https://www.cnblogs.com/khan724/p/4756228.html
Copyright © 2020-2023  润新知