划分树

问题

给定一个序列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\n", 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

posted @ 2016-12-17 17:35  ljt12138  阅读(124)  评论(0编辑  收藏  举报