POJ2104 区间第k大(版本1)

题目描述

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

题目大意

询问区间最第k大值。

数据范围

n<=100000, m<=100000,1<=L<=r<=n, 1<=k<=r-L+1

样例输入

7 2
1 5 2 6 3 7 4
1 5 3
2 7 1

样例输出

3
2

解题思路

  本来想写线段树套splay的,但想起以前的CQOI动态逆序对惨遭卡常数于是放弃了树套树其实是代码长不想写。最后可持久化权值线段树水过。最先离散化写错了居然都有80分。。。。

代码

#include <bits/stdc++.h>
#define Maxn 100005
using namespace std;
inline int Getint(){int x=0,f=1;char ch=getchar();while('0'>ch||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
struct node{
    int L,r,Sum,son[2],mid;
}Tree[2000005];
struct Lisan{
    int vl,pos,val;
}T[Maxn];
int ha[100005],rt[100005],n,m,cnt=0;
bool cmp1(Lisan a,Lisan b){return a.vl<b.vl;}
bool cmp2(Lisan a,Lisan b){return a.pos<b.pos;}
void Init(){
    n=Getint(),m=Getint();
    for(int i=1;i<=n;i++)
        T[i]=(Lisan){Getint(),i,0};
    sort(T+1,T+n+1,cmp1);
    for(int i=1;i<=n;i++)T[i].val=T[i].vl==T[i-1].vl?T[i-1].val:T[i-1].val+1;
    sort(T+1,T+n+1,cmp2);
    for(int i=1;i<=n;i++)ha[T[i].val]=T[i].vl;
}
void Build(int &v,int L,int r){
    Tree[v=++cnt]=(node){L,r,0,0,0,(L+r)/2};
    if(L==r)return;
    Build(Tree[v].son[0],L,(L+r)/2);
    Build(Tree[v].son[1],(L+r)/2+1,r);
}
void Insert(int &New,int Old,int vl){
    if(!Old)return;
    Tree[New=++cnt]=Tree[Old];
    Tree[New].Sum++;
    if(vl<=Tree[New].mid)Insert(Tree[New].son[0],Tree[Old].son[0],vl);
    else Insert(Tree[New].son[1],Tree[Old].son[1],vl);
}
int Ask(int New,int Old,int k){
    if(!k)return Tree[New].L;
    if(Tree[New].L==Tree[New].r)return Tree[New].L;
    int Lsize=Tree[Tree[New].son[0]].Sum-Tree[Tree[Old].son[0]].Sum;
    if(k>Lsize)return Ask(Tree[New].son[1],Tree[Old].son[1],k-Lsize);
    return Ask(Tree[New].son[0],Tree[Old].son[0],k);
}
int main(){
    Init();
    Build(rt[0],1,n);
    for(int i=1;i<=n;i++)Insert(rt[i],rt[i-1],T[i].val);
    while(m--){
        int L=Getint(),r=Getint(),k=Getint();
        cout<<ha[Ask(rt[r],rt[L-1],k)]<<"\n";
    }
    return 0;
}

转载于:https://www.cnblogs.com/Cedric341561/p/6811019.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值