zoukankan      html  css  js  c++  java
  • [POJ2104]K-th Number

    K-th Number

    题面

    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

    思路

    提取一下,这是一个区间k小值查询问题,也是一道主席树的经典例题。

    首先,我们可以引入一个权值线段树的概念,即第k个叶子节点存储的是第k个数的个数(当然,数据要离散化)。那么区间 ([l,r]) 则代表了排名为l的数到排名为r的数这个区间内共有多少个数。也就是说,权值线段树可以在 (log_{2} n) 的时间内查询到数列k小值。

    我们可以在权值线段树的的基础上加入前缀和的概念。我们可以建立n个线段树,第i个线段树包含的数据为 ([1,i]) 。假设我们需要求区间 ([l,r]) 的k小值。那么我们可以建立一颗新的线段树,把每个节点用第 (r) 棵线段树上这个节点的值减去第 (l-1) 棵线段树上这个节点的值。可以理解为第 (l-1) 棵线段树上所有点的值是插入[1,l-1]是更改的,为了屏蔽掉l之前的操作,我们用前缀和的思路把l之前操作带来的权值改变滤除即可。

    很显然,建立n个线段树在空间上和时间上都是不现实的。另外添加一个新的线段树的操作实质是添加一个新的点。而添加一个点只会对修改路径上的 (log_{2} n) 个点产生影响,那我们只需要新加入被修改的点即可。同时维护一个根节点列表,记录每个版本的根节点即可。

    查询的时候,同时从两个版本(即l-1和r)的根同时开始遍历,动态利用前缀和思想计算每个节点在两个节点中的差值。(其与构造一个l-1到r的线段树等效)。然后用与平衡树查询类似方法查询即可。

    代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #define maxn (int)(1e5+1000)
    int ls[maxn<<5],rs[maxn<<5],sum[maxn<<5],n,m,root[maxn],idx;
    using namespace std;
    int a[maxn],sorted[maxn],len;
    int getid(int a){
    	return lower_bound(sorted+1,sorted+1+len,a)-sorted;
    }
    int build(int l,int r){
    	int now=++idx;
    	if(l==r)return now;
    	int mid=(l+r)>>1;
    	ls[now]=build(l,mid);
    	rs[now]=build(mid+1,r);
    	return now;
    }
    int insert(int num,int l,int r,int mark){
    	int now=++idx;
    	rs[now]=rs[mark];ls[now]=ls[mark];sum[now]=sum[mark]+1;
    	if(l==r)return now;
    	int mid=(l+r)>>1;
    	if(num<=mid){
    		ls[now]=insert(num,l,mid,ls[mark]);
    	}
    	else{
    		rs[now]=insert(num,mid+1,r,rs[mark]);
    	}
    	return now;
    }
    int query(int ln,int rn,int l,int r,int kth){
    	if(l==r)return l;
    	int mid=(l+r)>>1;
    	int size=sum[ls[rn]]-sum[ls[ln]];
    	if(kth<=size){
    		return query(ls[ln],ls[rn],l,mid,kth);
    	}
    	else{
    		return query(rs[ln],rs[rn],mid+1,r,kth-size);
    	}
    	return 0;
    }
    int main(){
    	//freopen("in","r",stdin);
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++){scanf("%d",&a[i]);sorted[i]=a[i];}
    	sort(sorted+1,sorted+1+n);
    	len=unique(sorted+1,sorted+1+n)-sorted-1;
    	root[0]=build(1,len);
    	for(int i=1;i<=n;i++){
    		root[i]=insert(getid(a[i]),1,len,root[i-1]);
    	}
    	for(int i=1;i<=m;i++){
    		int l,r,k;scanf("%d%d%d",&l,&r,&k);
    		printf("%d
    ",sorted[query(root[l-1],root[r],1,len,k)]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    day09总结
    day09作业
    删除循环中选中的id
    数组套数组 获取数据
    vue for each循环出来数组的某一项 再重新合并成一个新数组
    全是没见过的
    vue element 分页
    解决iphonex屏幕过长背景图片或者放在元素里面的图片不能铺满的问题
    实在自动现在APK,微信跳浏览器下载
    js判断是否在微信浏览器中打开
  • 原文地址:https://www.cnblogs.com/GavinZheng/p/10812766.html
Copyright © 2011-2022 走看看