标签:
| Time Limit: 6000MS | Memory Limit: 65536K | |
| Total Submissions: 17679 | Accepted: 5561 |
Description
Input
Output
Sample Input
7 2 1 5 2 6 3 7 4 1 5 3 2 7 1
Sample Output
3 2
Source
/*
* Copyright (c) 2016, 烟台大学计算机与控制工程学院
* All rights reserved.
* 文件名称:k.cpp
* 作 者:单昕昕
* 完成日期:2016年4月15日
* 版 本 号:v1.0
*/
#include <iostream>
#include <stdio.h>
#include <algorithm>
const int maxn = 100005;
using namespace std;
int sor[maxn];//借助sort排序的数组
struct node
{
int num[maxn];//当前层的数
int cnt[maxn];
//cnt[]数组是划分树的核心部分
//保存每一个元素的左边的元素中位于下一层左子树的个数
} tree[40];//40是树的层数
//建树代码如下
void buildtree(int l, int r, int d)//d是深度
{
if (l == r)//递归出口
{
return;
}
int mid = (l+r)>>1;//划分左右区间
int opleft = l, opright = mid+1;//对左右子树的操作位置的初始化
int same_as_mid = 0;//和sor[mid]相同的数的数目
//计算在mid左边有多少个和sor[mid]相同的数(包括mid),都要放到左子树
for (int i = mid; i > 0; i--)
{
if (sor[i] == sor[mid])
same_as_mid++;
else
break;
}
int cnt_left = 0;//被划分到左子树的个数
for (int i = l; i <= r; i++)
{
//从l到r开始遍历
if (tree[d].num[i] < sor[mid])//左
{
tree[d+1].num[opleft++] = tree[d].num[i];
cnt_left++;
tree[d].cnt[i] = cnt_left;
}
else if(tree[d].num[i] == sor[mid] && same_as_mid)
{
//相同的都放在左子树
tree[d+1].num[opleft++] = tree[d].num[i];
cnt_left++;
tree[d].cnt[i] = cnt_left;
same_as_mid--;
}
else//右
{
tree[d].cnt[i] = cnt_left;
tree[d+1].num[opright++] = tree[d].num[i];
}
}
//递归建树
buildtree(l, mid, d+1);
buildtree(mid+1, r, d+1);
}
int query(int l, int r, int d, int ql, int qr, int k)
//1 n 0 a b k
//在d层[l,r]的节点里查找[a,b]中的第k小值
{
if (l == r)//递归出口
return tree[d].num[l];
int mid = (l+r)>>1;
int sum_in_left;//区间内元素位于下一层左子树的个数
int left;//[l,ql-1]左边的元素中位于下一层左子树的个数
if (ql == l)
{//如果ql是节点的左边界则有cnt[qr]个数进入左子树
sum_in_left = tree[d].cnt[qr];
left = 0;
}
else
{//如果ql不是节点的左边界则有cnt[qr]-cnt[ql-1]个数进入了左子树
sum_in_left = tree[d].cnt[qr] - tree[d].cnt[ql-1];
left = tree[d].cnt[ql-1];
}
if (sum_in_left >= k)
{//要找的点在左子树
//确定下一步询问的位置:
//如果在ql的左边有left个进入左子树
//那么ql到qr中第一个进入左子树的必定在l+left的位置
int new_ql = l+left;
int new_qr = new_ql+sum_in_left-1;
return query(l, mid, d+1, new_ql, new_qr, k);
}
else//要找的点在右子树
{
//确定下一步询问的位置
int a = ql - l - left;//表示当前区间左半部分即[l,ql-1]中在下一层是右孩子的个数
int b = qr - ql + 1 - sum_in_left;//表示当前区间右半部分即[ql,qr]中在下一层是右孩子的个数
int new_ql = mid + a + 1;
int new_qr = mid + a + b;
//k-sum_in_left表示要减去区间里已经进入左子树的个数
return query(mid+1, r, d+1, new_ql, new_qr, k - sum_in_left);
}
}
int main()
{
int n,m,i,a,b,k;
scanf("%d%d",&n,&m);
for(i=1; i<=n; ++i)
{
scanf("%d",&sor[i]);//先插入到sor数组
tree[0].num[i]=sor[i];//再插入第一层
}
sort(sor+1,sor+n+1);//升序排列
buildtree(1,n,0);//建树
for(i=1; i<=m; ++i)
{//查询
scanf("%d%d%d",&a,&b,&k);
printf("%d\n",query(1,n,0,a,b,k));
}
return 0;
}
POJ 2761-Feed the dogs(划分树)求区间内第k小的数
标签:
原文地址:http://blog.csdn.net/mikasa3/article/details/51329045