poj2104 线段树+二分搜索

 

如题:http://poj.org/problem?id=2104

 

K-th Number
Time Limit: 20000MS Memory Limit: 65536K
Total Submissions: 39943 Accepted: 13042
Case Time Limit: 2000MS

Description

You are working for Macrohard company in data structures department. After failing your previous task about key insertion you were asked to write a new data structure that would be able to return quickly k-th order statistics in the array segment.
That is, given an array a[1...n] of different integer numbers, your program must answer a series of questions Q(i, j, k) in the form: "What would be the k-th number in a[i...j] segment, if this segment was sorted?"
For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3). The segment a[2...5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6), the third number is 5, and therefore the answer to the question is 5.

Input

The first line of the input file contains n --- the size of the array, and m --- the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000).
The second line contains n different integer numbers not exceeding 10 9 by their absolute values --- the array for which the answers should be given.
The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).

Output

For each question output the answer to it --- the k-th number in sorted a[i...j] segment.

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

This problem has huge input,so please use c-style input(scanf,printf),or you may got time limit exceed.

Source

Northeastern Europe 2004, Northern Subregion

 

 

 

题意很简单,求闭区间内的第k大的数。

 

思路:关于区间的算法,我能想到的只有线段树,怎么更改线段树的区间的操作,查询才能满足这一道题。

1.首先,建树,每一个区间是一个排好序的数组,记录的是这个区间所有元素从小到大排列。建树的过程和归并排序的过程非常相似。建好子树后,对父节点进行更新,也就是对两个儿子的数组进行从小到大的合并、

2.开一个数组num 它是输入的N个数,并把它排序。

3.我们想想看,假设一个区间的第k大的那个数是x,也就是说要这个区间里小于等于x个数不超过k个。那么思路来了,我们枚举x(二分,初始lb=-1,rb=n(尽量取取不到的值))(x∈num数组),判断在给出区间内不超过x的数的个数,如果这个返回的个数>=k,也就是说这个枚举的x(num[mid])还偏小,让rb=mid,继续搜索,反之,lb=mid。

判断这个区间内不小于x的数的个数就要用到线段树了,如果当前搜索的区间和给出区间没有交集,返回0.如果有交集,搜索有交集的子区间。累加返回。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 100005

int n,m;
int nums[MAXN];

struct node
{
 int l,r;
 int *a;
};
node tree[MAXN*4];

void push_up(int p)
{
 int len=tree[p].r-tree[p].l+1;
 tree[p].a=new int[len];
 int p1=0,p2=0;
 int len1=tree[p*2].r-tree[p*2].l+1;
 int len2=tree[p*2+1].r-tree[p*2+1].l+1;
 int m=0;
 while(p1<len1&&p2<len2)
 {
  if(tree[p*2].a[p1]<tree[p*2+1].a[p2])
   tree[p].a[m++]=tree[p*2].a[p1++];
  else
   tree[p].a[m++]=tree[p*2+1].a[p2++];
 }
 int i;
 if(p1<len1)
  for(i=p1;i<len1;i++)
   tree[p].a[m++]=tree[p*2].a[i];
 else
  for(i=p2;i<len2;i++)
   tree[p].a[m++]=tree[p*2+1].a[i];
}
int c=0;
void build(int p,int l,int r)
{
 tree[p].l=l;
 tree[p].r=r;
 if(l==r)
 {
  tree[p].a=new int[1];
  scanf("%d",&tree[p].a[0]);
  nums[c++]=tree[p].a[0];
  return;
 }
 int mid=(l+r)/2;
 build(p*2,l,mid);
 build(p*2+1,mid+1,r);
 push_up(p);
}
int query(int p,int l,int r,int x)
{
 if(tree[p].l>r||tree[p].r<l)
  return 0;
 if(tree[p].l==l&&tree[p].r==r)
 {
  int len=tree[p].r-tree[p].l+1;
  return upper_bound(tree[p].a,tree[p].a+len,x)-tree[p].a;
 }
 int mid=(tree[p].l+tree[p].r)/2;
 if(l>mid)
  return query(p*2+1,l,r,x);
 else if(r<=mid)
  return query(p*2,l,r,x);
 else
 {
  int lc=query(p*2,l,mid,x);
  int rc=query(p*2+1,mid+1,r,x);
  return lc+rc;
 }
}
int main()
{
// freopen("C:\\1.txt","r",stdin);
 cin>>n>>m;
 build(1,1,n);
 sort(nums,nums+n);
 while(m--)
 {
  int l,r,k;
  scanf("%d%d%d",&l,&r,&k);
  int lb=-1,rb=n;
  while(rb-lb>1)
  {
   int md=(rb+lb)/2;
   int res=query(1,l,r,nums[md]); //查询[l,r]中不超过nums[md]的个数.
   if(res>=k)
    rb=md;
   else
    lb=md;
  }
 printf("%d\n",nums[rb]);
 }
 return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值