#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstdio>
using namespace std;
const int maxn=1e6+10;
//root数组代表第i颗树的根
//插入和查询都是 两棵树同步走相对应的节点
int cnt,root[maxn],n,m;
struct node
{
int lson,rson,num;
//分别是左子下标 右子下标 这段值域出现的个数
}tree[maxn*20];//每个点开logn个节点 20倍maxn差不多了
//pre代表上一颗子树的根 cur代表当前子树的根 v代表要插的值
void pushup(int pos)
{
tree[pos].num=tree[tree[pos].lson].num+tree[tree[pos].rson].num;
}
void update(int pre,int cur,int l,int r,int v)
{
if(l==r)//前缀和思想,第i颗树包含了前i个元素,值即权值线段树的操作
{
tree[cur].num=tree[pre].num+1;
return;
}
int mid=(l+r)>>1;
if(v<=mid)//新值在左子树里 新开一个左子节点
{
tree[cur].lson=++cnt;
tree[cur].rson=tree[pre].rson;//共用右子
update(tree[pre].lson,tree[cur].lson,l,mid,v);//递归左子区间
}
else//新值在右子树里 新开一个右子节点
{
tree[cur].rson=++cnt;
tree[cur].lson=tree[pre].lson;
update(tree[pre].rson,tree[cur].rson,mid+1,r,v);
}
pushup(cur);
}
int query(int L,int R,int l,int r,int k)//区间第k大
{
if(l==r) return l;//二分区间直至单点满足 不满足返回0
int mid=(l+r)>>1;
if(tree[tree[R].lson].num-tree[tree[L].lson].num>=k)
return query(tree[L].lson,tree[R].lson,l,mid,k);
else if(tree[tree[R].rson].num-tree[tree[L].rson].num>=k-tree[tree[R].lson].num+tree[tree[L].lson].num)
return query(tree[L].rson,tree[R].rson,mid+1,r,k-tree[tree[R].lson].num+tree[tree[L].lson].num);
return -1;//没有k个数
}
int a[maxn],b[maxn],t;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
int v;
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+1+n);
t=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++)
{
root[i]=++cnt;
int u=lower_bound(b+1,b+1+t,a[i])-b;
update(root[i-1],root[i],1,t,u);
}
//前缀和思想 [l,r]信息只需将第root[r]棵树与第root[l-1]棵树作差
//以下为bzoj3524 问[l,r]内出现次数超过一半的数是否存在
while(m--)
{
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",b[query(root[l-1],root[r],1,t,k)]);
}
return 0;
}
主席树(可持久化线段树)
最新推荐文章于 2025-04-12 21:31:43 发布