zoukankan      html  css  js  c++  java
  • 划分树

    问题

    给定一个序列a1,a2an,对于若干组询问(l,r,k),输出区间[l,r]上第k大的数。

    这个问题有许多种解决方法。比如分块成n然后暴力维护,运用树套树,或是最经典的O(n)的kth算法。但这些方法的时间复杂度分别为O(mnlgn)O(mlgnlgn)O(nm),往往不能满足我们的需求。对此,一种数据结构“划分树”就可以派上用场了。它适用于没有修改的区间kth问题,总的时间复杂度为O(nlgn),比之前的各位高到不知哪里去。

    思路

    主要思路就是模拟快速排序的过程。树的第一层就是原始数据,而之后的每一层就是进行了一层快排划分之后的结果。

    图源水印

    几个细节

    1. 可以用一个标记表明当前元素在下一层中的位置,边界一定要注意。
    2. 重复数据要用*(n+1)+i的方法除去,否则会有麻烦
    3. 注意负数!!!!
    4. POJ是多测…………..

    Code

    //#include <bits/stdc++.h>
    // 划分树模板
    #include <stdio.h>
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <cctype>
    #include <vector>
    #include <cstdlib>
    #include <ctime>
    using namespace std;
    
    //#define scanf scanf_s
    typedef long long ll;
    
    ll sor[100005];
    int n, m;
    ll tree[30][100005];
    int lp[30][100005], rp[30][100005];
    
    void build_tree(int lev, const int l, const int r)
    {
        if (l >= r) return;
        ll mid = sor[(l + r) >> 1];
        int i = l, j = ((l + r) >> 1) + 1;
        for (int k = l; k <= r; k++) {
            if (tree[lev][k] <= mid) {
                tree[lev + 1][i] = tree[lev][k];
                lp[lev][k] = i; rp[lev][k] = j; // 标记
                i++;
            }
            else {
                tree[lev + 1][j] = tree[lev][k];
                lp[lev][k] = i; rp[lev][k] = j;
                j++;
            }
        }
        build_tree(lev + 1, l, ((l + r) >> 1));
        build_tree(lev + 1, ((l + r) >> 1) + 1, r);
    }
    
    ll query(int lev, int l, int r, int k, int lr = 1, int rr = n) // lr rr的传入方便特判边界
    {
        if (l == r) return tree[lev][l];
        ll mid = sor[(lr + rr) >> 1];
        int ls = lp[lev][l], le = lp[lev][r] - 1; if (tree[lev][r] <= mid) le++;//特判边界
        int lnum = le - ls + 1;                                                 //
        int rs = rp[lev][l], re = rp[lev][r] - 1; if (tree[lev][r] > mid) re++; //极其重要
        int rnum = re - rs + 1;
        if (lnum >= k)
            return query(lev + 1, ls, le, k, lr, (lr + rr) >> 1);
        else
            return query(lev + 1, rs, re, k - lnum, ((lr + rr) >> 1) + 1, rr);
    }
    
    int main()
    {
        //freopen("dat.in", "r", stdin);
        //freopen("dat.out", "w", stdout);
        while (scanf("%lld%lld", &n, &m) != EOF) {
            memset(tree, 0, sizeof tree);
            memset(lp, 0, sizeof lp);
            memset(rp, 0, sizeof rp);
            for (int i = 1; i <= n; i++) {
                scanf("%lld", &tree[0][i]);
                (tree[0][i] *= (n + 1)) += i;
                if (tree[0][i] < 0) tree[0][i] -= 2 * i;// 处理负数
                sor[i] = tree[0][i];
            }
            sort(sor + 1, sor + n + 1);
            build_tree(0, 1, n);
            for (int i = 1; i <= m; i++) {
                int l, r, k;
                scanf("%d%d%d", &l, &r, &k);
                printf("%lld
    ", query(0, l, r, k) / (n + 1));
            }
        }
        return 0;
    }

    Links

    http://blog.csdn.net/zxy_snow/article/details/6681086
    http://blog.csdn.net/shiqi_614/article/details/8041390

  • 相关阅读:
    响应式的WEB设计
    WPF的抽奖程序
    使用Visual Studio 利用WinGDB编译和远程调试嵌入式Linux的程序
    Sqler 工具帮你解决(更新部分监控)
    YSlow相关规则的调优工具和方法
    wcf基础笔记
    HTTP协议之状态码详解
    网站首页图片滚动显示
    CodeLove初版发布
    HDFS学习– Namenode and Datanode
  • 原文地址:https://www.cnblogs.com/ljt12138/p/6684358.html
Copyright © 2011-2022 走看看