hdu 4251 The Famous ICPC Team Again

hdu 4251 The Famous ICPC Team Again 划分树
//hdu 4251 The Famous ICPC Team Again
//划分树

//求中位数(就是求中间大的)
//建树和查找,模板题,看代码
//不懂可以看看这人写的
//http://www.cnblogs.com/183zyz/archive/2012/08/01/2618295.html

#define infile freopen("in.txt", "r", stdin);
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define N 100005

using namespace std;

struct NODE
{
    int l, r;
}tree[4*N];


int sorted[N], level[20][N];//level记录每一层每个段的序列
int l_cnt[20][N];   //记录left到right中进入

void build(int left, int right, int loc, int dep)
{
    tree[loc].l = left;
    tree[loc].r = right;
    if(left == right)
        return;

    int mid = left + (right-left)/2;

    int mid_val = sorted[mid];

    int cnt = 0;    //记录小于left到right这段的中值的个数
    for(int i = left; i <= right; ++i)
        if(level[dep][i] < mid_val)
            cnt++;

    //这里l_tot是从left开始···
    int l_tot = left, r_tot = mid + 1;
    int equal_cnt = 0;  //equal_cnt记录跟mid_val相等且进入左子树的个数
    for(int i = left; i <= right; ++i)
    {
        if(level[dep][i] < mid_val) //小于mid_val的进入左子树
            level[dep+1][l_tot++] = level[dep][i];
        else if(level[dep][i] == mid_val && mid-left+1 - cnt > equal_cnt)
        {   //等于mid_val的且没超过左子树限定个数的时候进入左子树
            equal_cnt++;
            level[dep+1][l_tot++] = level[dep][i];
        }
        else
            level[dep+1][r_tot++] = level[dep][i];
        l_cnt[dep][i] = l_tot - left;   //记录进入左子树的个数
    }
    build(left, mid, 2*loc, dep+1);
    build(mid+1, right, 2*loc+1, dep+1);
}

int ans;
void find(int loc, int from, int to, int dep, int aim)
{
    int left = tree[loc].l, right = tree[loc].r;
    if(left == right)
    {
        ans = level[dep][left];
        return;
    }
    int mid = left + (right-left)/2;

    //l_from记录当前层中,from以前的数进入左子树的个数(不包括from这个数)
    //l_to记录当前层中,to以前的数进入又子树的个数,包括to
    int l_from, l_to = l_cnt[dep][left+to-1];
    if(from > 1)
        l_from = l_cnt[dep][from+left-2];
    else
        l_from = 0;

    //接下去那层就应该保持和这层一样的起点,即from左边的个数不变
    //若下一层到左子树,则要从from以左的进入左子树的个数(from_cnt)开始
    //由于from这个数也到左子树所以下一层应从from_cnt+1开始
    //若下一层到右子树要去掉进入左子树的个数
    if(l_to - l_from >= aim)    //from到to之间进入左子树的个数
        find(2*loc, l_from+1, l_to, dep+1, aim);
    else
        find(2*loc+1, from-1-l_from+1, to-l_to, dep+1, aim-(l_to-l_from));
}

int main()
{
    //infile
    int n, n_case = 1;;
    while(scanf("%d", &n) != EOF)
    {
        printf("Case %d:\n", n_case++);
        for(int i = 1; i <= n; ++i)
        {
            scanf("%d", &level[1][i]);
            sorted[i] = level[1][i];
        }
        sort(sorted+1, sorted+n+1);
        build(1, n, 1, 1);
        int n_query;
        scanf("%d", &n_query);
        while(n_query--)
        {
            int from, to;
            scanf("%d%d", &from, &to);
            find(1, from, to, 1, (to-from)/2+1);
            printf("%d\n", ans);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/gabo/p/2634353.html