poj2104(主席树)

K-th Number

Time Limit: 20000MS   Memory Limit: 65536K
Total Submissions: 35704   Accepted: 11396
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 109 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小元素,可以用划分树,在我的博客poj2104划分树解法有划分树的解法,至于划分树可以参考划分树

本题要用到的主席树相关知识在主席树读书笔记可以找到。主席树的每个节点对应一颗线段树,每个节点的意义为离散后原序列的某个后缀。在每棵线段树中若元素出现则标记为1,问题可以转化为求区间的前k个数,第k个数即为第k小的元素,在区间上操作是线段树的特色。

贴段代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;

const int MAXN=100000+100;
const int MAXM=MAXN*20;

int tot,n,m;
int da[MAXN],sDa[MAXN];
int leftChild[MAXM],rightChild[MAXM],wei[MAXM],chairTNode[MAXM];

/**********************************
*参数:待处理元素区间
*功能:建立一棵空线段树
*返回值:返回根节点下标
***********************************/
int Build(int left,int right)
{
	int id=tot++;
	wei[id]=0;
	if(left<right)
	{
		int mid=(left+right)>>1;
		leftChild[id]=Build(left,mid);
		rightChild[id]=Build(mid+1,right);
	}
	return id;
}

int Update(int root,int pos,int val)
{
	int l=1,r=m,mid,newRoot=tot++,retRoot=newRoot;
	wei[newRoot]=wei[root]+val;
	while(l<r)
	{
		mid=(l+r)>>1;
		if(pos<=mid)
		{
			//确定节点孩子节点
			leftChild[newRoot]=tot++;
			rightChild[newRoot]=rightChild[root];

			//确定待跟新节点以及历史版本
			newRoot=leftChild[newRoot];
			root=leftChild[root];

			r=mid;
		}
		else
		{
			rightChild[newRoot]=tot++;
			leftChild[newRoot]=leftChild[root];
			newRoot=rightChild[newRoot];
			root=rightChild[root];
			l=mid+1;
		}
		wei[newRoot]=wei[root]+val;
	}
	return retRoot;
}

int Query(int leftRoot,int rightRoot,int k)
{
	int  l=1,r=m,mid;
	while(l<r)
	{
		mid=(l+r)>>1;
		if(wei[leftChild[leftRoot]]-wei[leftChild[rightRoot]]>=k)//第k小值在左子树
		{
			//确定查找新区间
			leftRoot=leftChild[leftRoot];
			rightRoot=leftChild[rightRoot];

			r=mid;
		}
		else
		{
			k-=wei[leftChild[leftRoot]]-wei[leftChild[rightRoot]];
			leftRoot=rightChild[leftRoot];
			rightRoot=rightChild[rightRoot];
			l=mid+1;
		}
	}
	return l;
}

int main()
{
	int q,i;
	int ql,qr,k;
	while(scanf("%d%d",&n,&q)!=EOF)
	{
		m=0;
		tot=0;
		for(i=1;i<=n;i++)
		{
			scanf("%d",&da[i]);
			sDa[i]=da[i];
		}
		sort(sDa+1,sDa+n+1);
		m=unique(sDa+1,sDa+1+n)-sDa-1;
		chairTNode[n+1]=Build(1,m);
		//cout<<"**********"<<endl;

		for(i=n;i>=1;i--)
		{
			int pos=lower_bound(sDa+1,sDa+1+m,da[i])-sDa;
			chairTNode[i]=Update(chairTNode[i+1],pos,1);
		}
		while(q--)
		{
			scanf("%d%d%d",&ql,&qr,&k);
			printf("%d\n",sDa[Query(chairTNode[ql],chairTNode[qr+1],k)]);
		}
	}
	system("pause");
	return 0;
}

poj2104(主席树),布布扣,bubuko.com

时间: 2024-10-01 19:52:45

poj2104(主席树)的相关文章

poj2104 主席树 区间K大 在线 无修改

关于主席树: 主席树(Chairman Tree)是一种离线数据结构,使用函数式线段树维护每一时刻离散之后的数字出现的次数,由于各历史版本的线段树结构一致,可以相减得出区间信息,即该区间内出现的数字和对应的数量,由于在线段树内,左子树代表的数字都小与右子树,便可像平衡树一样进行K大询问.新建一颗树是\(O(logn)\),查询一次也为\(O(logn)\). 比划分树好想&写多了,但是在POJ上比划分树慢一些. CODE: 1 #include <cstdio> 2 #include

POJ2104主席树模板题

完成新成就——B站上看了算法https://www.bilibili.com/video/av4619406/?from=search&seid=17909472848554781180#page=2 K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 60158   Accepted: 21054 Case Time Limit: 2000MS Description You are working

poj2104(主席树讲解)

今天心血来潮,突然想到有主席树这个神奇的玩意儿...一直都只是听说也没敢看.(蒟蒻蛋蛋的忧伤...) 然后到网上翻大神的各种解释...看了半天... 一拍脑袋...哇其实主席树 真的难...[咳咳我只是来搞笑的] 看了很多种解释最后一头雾水啊...就是没法脑补出(嗯没错经常脑补数据结构长啥样)主席树的样子.... 最后终于找到了一个大大大大大大神犇的ppt,看到了主席树的真面目,才真的能弄懂主席树的结构... -------------------------------------------

【POJ2104】K-th Number——主席树

早上刷NOIP的题刷到有点烦就想学点新东西,然后.....一个早上就这样过去了QAQ.虽然主席树不是NOIP考点,但是...或许我能活到省选呢?(美好的幻想) 题目链接 题目的大意就是给定一个长度为n的区间,给出m个询问,每次询问一个区间[l,r]中第k小的树. 主席树(一种可持久化线段树)的入门题. 推荐一发学习资料:戳这里 感觉人家讲得很仔细了我也没什么讲的必要了...... 总算是学了一种可持久化树了,好像也没想象中那么难?这道题的重点在query函数方面,建议自己在纸上模拟一下建树和查询

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include

POJ2104 K-th Number[主席树]

K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 51440   Accepted: 17594 Case Time Limit: 2000MS Description You are working for Macrohard company in data structures department. After failing your previous task about key inse

POJ2104 K-th Number(主席树)

题意: 静态区间第K大 思路: 之前学划分树的时候当了模版练了, 感觉划分树真是不该学.. 又拿来练主席树吧 /* *********************************************** Author :devil ************************************************ */ #include <cstdio> #include <cstring> #include <iostream> #include

[主席树]HDOJ2665 &amp;&amp; POJ2104 &amp;&amp; POJ2761

主席树真是神奇的物种! 题意:给n.m   下面有n个数 (编号1到n) 有m个询问,询问的是上面的数的编号在[l,r]之间第k小的数 n.m的范围都是1e5 是主席树的入门题 借此来学习一下主席树 1 const int N=1e5+5; 2 int L[N<<5], R[N<<5], sum[N<<5]; 3 int tot; 4 int a[N], T[N], Hash[N]; 5 int build(int l, int r) 6 { 7 int rt=(++t

主席树POJ2104

求区间第k大数是多少 用我惯用的线段树写法似乎不适合写主席树,看别人的代码好半天才看懂 用root表示每一个前缀树的根,思想大致是由第i-1个树构成的树建第i个树,由于加入一个数只需要log级别修改,所以建树效率很高. 主席树的精髓在于可持续化,就是说之前区间的信息不去修改它,递推加入新元素的时候,要新开log个空间来存储.因为主席树本身比较占用空间,只需改变这些空间的指向就可以重复利用不变的空间,达到节省空间的目的. 简而言之,主席树是一种重复利用数据不变的空间,建成空间上只有一颗完整的树,但