| Time Limit: 20000MS | Memory Limit: 65536K | |
| Total Submissions: 40920 | Accepted: 13367 | |
| Case Time Limit: 2000MS | ||
Description
Input
Output
Sample Input
7 3 1 5 2 6 3 7 4 2 5 3 4 4 1 1 7 3
Sample Output
5 6 3
Hint
算法及题目分析:给你一个具有n个数字的数组,进行m此询问。每次询问,都会给你一个子区间,问你这个区间内的第k大的数字是多少?例如:数组序列(下标从1开始)是:1 5 2 6 3 7 4, 询问:[2, 5]的第3大的数字。[2, 5]区间的数是:5 2 6 3,排序后:2 3 5 6,第3大的数应该是5. 但是当数据量比较庞大的时候,询问次数比较多的时候,这种常规算法思想实现起来之星效率比较低。于是一种树型结构算法应运而生:【划分树算法】(基于线段树思想)(划分树实现细节有待补充)。【可以参考:程序设计 解题策略 吴永辉 王建德 编著 P2】
代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <string>
#include <iostream>
#include <algorithm>
#define N 100000+10
using namespace std;
//POJ 2104
int tree[50][N];//tree[p][i]表示第p层中第i的位置的值
int sorted[N];
int toleft[50][N];//toleft[p][i]表示第p层从1到i中有
//多少个数被划分入下一层的左子区间
//划分树建树的实现过程
void build(int ll ,int r, int dep)
{
if(ll==r)
return; //递归出口 若划分至叶子,则回溯
int mid=(ll+r)>>1; //等于(l+r)/2 计算区间的中间指针
int same=mid-ll+1; //计算[l,r]被分入下层左区间的个数
for(int i=ll; i<=r; i++)
{
if(tree[dep][i] < sorted[mid])
same--;
}
int llpos=ll;
int rpos=mid+1;
for(int i=ll; i<=r; i++)
{
if(tree[dep][i] < sorted[mid])
tree[dep+1][llpos++]=tree[dep][i];
else if(tree[dep][i]==sorted[mid] && same>0 )
{
tree[dep+1][llpos++]=tree[dep][i];
same--;
}
else
tree[dep+1][rpos++]=tree[dep][i];
toleft[dep][i]=toleft[dep][ll-1] +llpos-ll;
}
build(ll, mid, dep+1);//递归计算下一层的左子区间
build(mid+1, r, dep+1);//递归计算下一层的右子区间
}
int query(int L, int R, int ll, int r, int dep, int k)
//从划分数的dep层出发 自上而下的在大区间[L,R]里查询子区间
//[ll,r]中第K大的数
{
if(ll==r)
return tree[dep][ll];
int mid=(L+R)>>1;
int cnt=toleft[dep][r]-toleft[dep][ll-1];
if(cnt >= k)
{
int newll=L+toleft[dep][ll-1]-toleft[dep][L-1];
int newr = newll+cnt-1;
return query(L, mid, newll, newr, dep+1, k);
}
else
{
int newr=r+toleft[dep][R]-toleft[dep][r];
int newll=newr-(r-ll-cnt);
return query(mid+1, R, newll, newr, dep+1, k-cnt);
}
}
int main()
{
int n, m;
int i;
while(~scanf("%d %d", &n, &m))
{
for(i=1; i<=n; i++)//n
{
scanf("%d", &tree[0][i]);//
sorted[i]=tree[0][i];
}
sort(sorted+1,sorted+n+1);
build(1, n, 0);//建立划分树
while(m--)
{
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
printf("%d\n", query(1, n, u, v, 0, w));
}
}
return 0;
}
POJ2104 K-th Number (子区间内第k大的数字)【划分树算法模板应用】
原文:http://www.cnblogs.com/yspworld/p/4515621.html