标签:poj2104 k-th number 归并树 划分树 求区间第k大数
题目链接:http://poj.org/problem?id=2104
题目意思很简单,就是给你一个序列,查询某区间第K大的数;
方法1:时间复杂度O(N*M);不支持更新操作,代码简单;
利用结构体排序,保留原数据的顺序。
#include <stdio.h>
#include <iostream>
#include <algorithm>
#define N 100000
using namespace std;
/*
这个思路很好;时间复杂度O(n*m);
不过还好这个题目给的数据量不大,而且时间限制给了2s,所以可以很轻松的过;
*/
struct NODE
{
int x,id;
}node[N];
bool cmp(NODE a,NODE b)
{
return a.x<b.x;
}
int main()
{
int i,j,n,m,a,b,k;
while(~scanf("%d%d",&n,&m))
{
for(i=0;i<n;i++)
{
scanf("%d",&node[i].x);
node[i].id=i;
}
sort(node,node+n,cmp);
for(i=0;i<m;i++)
{
scanf("%d%d%d",&a,&b,&k);
a--,b--;
for(j=0;j<n;j++)
{
if(node[j].id>=a&&node[j].id<=b) k--;
if(!k)
{
printf("%d\n",node[j].x);
break;
}
}
}
}
return 0;
}
方法2:归并树,时间复杂度O(nlogn+mlong^3(n));利用归并排序,用线段树位数数组顺序;
#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<queue>
#include<map>
#include<cmath>
#include<stack>
#include<set>
#include<vector>
#include<algorithm>
#define LL long long
#define inf 1<<30
#define s(a) scanf("%d",&a)
#define Clear(a,b) memset(a,b,sizeof(a))
using namespace std;
/*
O(nlogn+mlog^3(n))
*/
const int N=200005;
int n,m,a,b;
int seg[25][N];
int num[N];
void Merge_Sort(int l,int r,int deep) // 每一层保留该层已经排好的数据;
{
int mid=(l+r)>>1;
int i=l,j=mid+1,k=l;
while(i<=mid&&j<=r){
if(seg[deep+1][i]<=seg[deep+1][j]){
seg[deep][k++]=seg[deep+1][i++];
}else{
seg[deep][k++]=seg[deep+1][j++];
}
}
while(j<=r){
seg[deep][k++]=seg[deep+1][j++];
}
while(i<=mid){
seg[deep][k++]=seg[deep+1][i++];
}
}
void Build(int l,int r,int rt,int deep) // 建树;deep根节点的深度;
{
if(l==r){
seg[deep][l]=num[l]; // 单叶子节点按输入的顺序存数据;
return;
}
int mid=(l+r)>>1;
Build(l,mid,rt<<1,deep+1);
Build(mid+1,r,rt<<1|1,deep+1);
Merge_Sort(l,r,deep); // 归并排序;
}
int Query(int v,int deep,int L,int R,int l,int r,int rt) // 查询[L,R]区间比v小的数有多少个;
{
if(r<L||R<l) return 0;
if(L<=l&&R>=r){
return lower_bound(&seg[deep][l],&seg[deep][r]+1,v)-&seg[deep][l]; // 返回第一个大于等于v的下标;
}
int mid=(l+r)>>1;
return Query(v,deep+1,L,R,l,mid,rt<<1)+Query(v,deep+1,L,R,mid+1,r,rt<<1|1);
}
int Solve(int l,int r,int k){ // 二分查找[L,R],降低时间复杂度;
int L=1,R=n,mid;
while(L<R){
mid=(L+R+1)>>1; // 这里要特别注意;mid=(L+R+1)>>1;不是mid=(L+R)>>1;
int cnt=Query(seg[1][mid],1,l,r,1,n,1); // 返回比v小的个数;
if(cnt<=k){ // 如果小于等于我们要查找的k,说明seg[1][mid]这个数太小了,网右边查找;
L=mid;
}else{
R=mid-1;
}
}
return seg[1][L];
}
int main()
{
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;i++) s(num[i]);
Build(1,n,1,1);
while(m--){
int l,r,k;
s(l);s(r);s(k);
printf("%d\n",Solve(l,r,k-1)); // 这里也值得注意一下,输入的是k-1,不是k;
}
}
return 0;
}
方法3:划分树;好吧,这个思路我还没有整理好,给这个牛逼算法留一块空地;^_^....
版权声明:本文为博主原创文章,未经博主允许不得转载。
POJ2104-K-th Number-求区间第K大数(暴力or归并树or划分树)
标签:poj2104 k-th number 归并树 划分树 求区间第k大数
原文地址:http://blog.csdn.net/wlxsq/article/details/47404521