区间第k大(版本1)POJ2104(BSOI1722)

区间第k大(版本1)POJ2104

Description

给定一个长度为n的序列,m个询问,每个询问的形式为:L,r,k表示在[L,r]间中的第k大元素。

Input

第1行:2个数,n,m表示序列的长度和询问的个数
第2行:n个数,表示n个数的大小
第3-m+2行:每行3个数,L,r,k表示询问在[L,r]区间内第k小的元素

Output

对于每个询问,输出答案。

Sample Input

7 2

1 5 2 6 3 7 4

1 5 3

2 7 1

Sample Output

3

2

Hint

【数据范围】
  对于100%的数据,n<=100000, m<=100000,1<=L<=r<=n, 1<=k<=r-L+1

Solution

一道解法多种多样的区间模板题目。树套树,主席树,划分树,整体二分什么的都可以。。。

蒟蒻今天尝试的是主席树。

我们将一个区间变成n个森林,每一次都添加相应的点计入一颗新的权值线段树。我们查询[L,R]的区间第k大时,用第R个节点对应的树减去第L-1个节点对应的树,就得到了针对[L,R]区间的一颗线段树,然后就是用权值线段树的计数来统计第k大了。

CODE

时间吊打蒟蒻之前写的树套树。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=100005;
inline int read(){
	char c;int rec=0;
	while((c=getchar())<'0'||c>'9');
	while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
	return rec;
}
struct Persistent_Segtree{int F,s[2],L,R,sum;}tree[20*N];
struct Node{int id,val;}a[N];
int n,m,cnt=0;
int root[20*N];
inline int Build(int L,int R){
    int mid=(L+R)>>1,p=++cnt;
	tree[p].L=L;tree[p].R=R;tree[p].sum=0;
    if(L==R)return p;
    tree[p].s[0]=Build(L,mid);tree[p].s[1]=Build(mid+1,R);
    return p;
}
inline void Inherit(int v,int pre,int val){tree[v].sum=tree[pre].sum+val;
	tree[v].s[0]=tree[pre].s[0];tree[v].s[1]=tree[pre].s[1];
	tree[v].L=tree[pre].L;tree[v].R=tree[pre].R;return ;
}
inline int New_Edition(int pre,int v,int val){
	int p=++cnt;Inherit(p,pre,val);
	if(tree[p].L==v&&tree[p].R==v)return p;
	int mid=(tree[p].L+tree[p].R)>>1;
	tree[p].s[v>mid]=New_Edition(tree[pre].s[v>mid],v,val);
	return p;
}
inline void Ask(int p,int q,int pos){
	if(tree[p].L==tree[p].R){cout<<a[tree[p].L].val<<'\n';return ;}
	int t=tree[tree[p].s[0]].sum-tree[tree[q].s[0]].sum;
	Ask(tree[p].s[pos>t],tree[q].s[pos>t],pos-t*(pos>t));
}
inline bool cmp(Node x,Node y){return x.val<y.val;}
int main(){
	n=read();m=read();
	int i,x,y,k;
	root[0]=Build(1,n);
	for(i=1;i<=n;i++)a[i].val=read(),a[i].id=i;
	sort(a+1,a+1+n,cmp);for(i=1;i<=n;i++)root[a[i].id]=i;
	for(i=1;i<=n;i++)root[i]=New_Edition(root[i-1],root[i],1);
	for(i=1;i<=m;i++)x=read(),y=read(),k=read(),Ask(root[y],root[x-1],k);
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值