可持久化线段树 区间第k大

2018-04-04

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1175

一个长度为N的整数序列,编号0 - N - 1。进行Q次查询,查询编号i至j的所有数中,第K大的数是多少。

例如: 1 7 6 3 1。i = 1, j = 3,k = 2,对应的数为7 6 3,第2大的数为6。

Input

第1行:1个数N,表示序列的长度。(2 <= N <= 50000)
第2 - N + 1行:每行1个数,对应序列中的元素。(0 <= S[i] <= 10^9)
第N + 2行:1个数Q,表示查询的数量。(2 <= Q <= 50000)
第N + 3 - N + Q + 2行:每行3个数,对应查询的起始编号i和结束编号j,以及k。(0 <= i <= j <= N - 1,1 <= k <= j - i + 1)

Output

共Q行,对应每一个查询区间中第K大的数。

Input示例

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

Output示例

7
6
1
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cctype>
using namespace std;
inline int gi(){int d=0;char c=getchar();while(!isdigit(c))c=getchar();
while(isdigit(c)){d=(d<<3)+(d<<1)+c-‘0‘;c=getchar();}return d;}

const int N=50005;
const int M=2000005;
struct QUE{
    int l,r,i,k;
}q[M];
bool cmp1(QUE aa,QUE bb){
    return aa.r<bb.r;
}
int sm[M<<2],ls[M],rs[M],rt[M],tot;
int a[N],san[N],num[N],ans[N],cnt;
int n,Q;
#define gem int mi=(l+r)>>1

void gai(int old,int &o,int l,int r,int P,int C){
    o=++tot;
    ls[o]=ls[old];rs[o]=rs[old];sm[o]=sm[old]+C;
    if(l==r)return;
    gem;
    if(P<=mi)gai(ls[o],ls[o],l,mi,P,C);
    else gai(rs[o],rs[o],mi+1,r,P,C);//??
}
int qur(int old,int o,int l,int r,int k){
    if(l==r)return l;
    int sum=sm[rs[o]]-sm[rs[old]];
    gem;
    if(sum<k)return qur(ls[old],ls[o],l,mi,k-sum);
    else return qur(rs[old],rs[o],mi+1,r,k);
}
#define rep(xx,yy,zz) for(xx=yy;xx<=zz;++xx)
int main(){
    register int i,j;
    n=gi();
    rep(i,1,n){
        a[i]=gi();san[i]=a[i];
    }
    sort(san+1,san+n+1);
    rep(i,1,n)
        if(i==1||san[i]!=san[i-1])
            num[++cnt]=san[i];
    Q=gi();
    rep(i,1,Q){
        q[i].i=i;q[i].l=gi()+1;q[i].r=gi()+1;q[i].k=gi();
    }
    sort(q+1,q+Q+1,cmp1);

    j=1;
    rep(i,1,n){
        int x=lower_bound(num+1,num+cnt+1,a[i])-num;
        gai(rt[i-1],rt[i],1,cnt,x,1);
        for(;q[j].r==i;++j){
            ans[q[j].i]=qur(rt[q[j].l-1],rt[i],1,cnt,q[j].k);
        }
    }
    rep(i,1,Q){
        printf("%d\n",num[ans[i]]);
    }
    return 0;
}


原文地址:https://www.cnblogs.com/xln1111/p/8715097.html

时间: 2024-10-07 23:53:09

可持久化线段树 区间第k大的相关文章

hdu4348 - To the moon 可持久化线段树 区间修改 离线处理

法一:暴力! 让干什么就干什么,那么久需要可持久化线段树了. 但是空间好紧.怎么破? 不down标记好了! 每个点维护sum和add两个信息,sum是这段真实的和,add是这段整体加了多少,如果这段区间被完全包含,返回sum,否则加上add * 询问落在这段区间的长度再递归回答. 怎么还是MLE? 麻辣鸡指针好像8字节,所以改成数组的就过了... #include<cstdio> #include<cstring> #include<cstdlib> #include&

主席树区间第K大

主席树的实质其实还是一颗线段树, 然后每一次修改都通过上一次的线段树,来添加新边,使得每次改变就改变logn个节点,很多节点重复利用,达到节省空间的目的. 1.不带修改的区间第K大. HDU-2665 模板题 代码: 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt&qu

POJ 2104 K-th Number 主席树 区间第K大

今天第一次接触可持久化数据结构,还是有必要总结一下的. 首先对于查找第k大的问题,先搞清楚怎么样通过利用N颗线段树来求解.如果是求全局第K大,那么可以把数字的值作为位置插入线段树,然后通过区间和+二分来找到第k个位置.因为是通过区间和来找第k大的,显然是满足前缀和性质的,所以查询l,r区间的第k打,就直接根据1-l - 1,1-r两个区间建立两颗线段树,然后通过节点依次相减来求得第k大值.所以这样子解需要的内存空间是n*n*logn的(不需要管数的范围,范围再大也可以通过离散化缩小到n). 但是

PAT天梯赛练习题 L3-002. 堆栈(线段树查询第K大值)

L3-002. 堆栈 时间限制 200 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 大家都知道“堆栈”是一种“先进后出”的线性结构,基本操作有“入栈”(将新元素插入栈顶)和“出栈”(将栈顶元素的值返回并从堆栈中将其删除).现请你实现一种特殊的堆栈,它多了一种操作叫“查中值”,即返回堆栈中所有元素的中值.对于N个元素,若N是偶数,则中值定义为第N/2个最小元:若N是奇数,则中值定义为第(N+1)/2个最小元. 输入格式: 输入第一行给出正整

POJ2985 并查集+线段树 求第k大的数

其实这题之前做过,线段树一直不熟,所以也一直没有搞懂 本题的关键是线段树原始区间代表的是每一种容器(size不同)的数量 比如 刚开始都是互不相关的,所以1的容器有n个 2 3 4...为0个 线段树每个结点的附加信息是该区间的和 本题查找出的代码是关键 比如左右子树分别为sum 27 25 ,则第26大的容器必然在左子树上,继续递归下去,则要在该左子树找 (26-25)大的容器... 通俗讲 本题就是改变点修改 求和变化(附加信息)的情况 只是用k大转化了一下 线段树还是做的太少,近期还要加强

HDU 6278 主席树(区间第k大)+二分

Just h-index Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others)Total Submission(s): 438    Accepted Submission(s): 203 Problem Description The h-index of an author is the largest h where he has at least h papers wit

hdu 2665 可持久化线段树求区间第K大值(函数式线段树||主席树)

http://acm.hdu.edu.cn/showproblem.php?pid=2665 Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The first line is the number of the test cases. For each test case, the first line contain two integer n and m (

poj2104 求区间第k大 可持久化线段树

poj2104 求区间第k大  可持久化线段树 #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef

主席树(可持久化线段树) 静态第k大

可持久化数据结构介绍 可持久化数据结构是保存数据结构修改的每一个历史版本,新版本与旧版本相比,修改了某个区域,但是大多数的区域是没有改变的, 所以可以将新版本相对于旧版本未修改的区域指向旧版本的该区域,这样就节省了大量的空间,使得可持久化数据结构的实现成为了可能. 如下图,就是可持久化链表 插入前 插入后 尽可能利用历史版本和当前版本的相同区域来减少空间的开销. 而主席树(可持久化线段树)的原理同样是这样. 有n个数字,  我们将其离散化,那么总有[1,n]个值,如果建一棵线段树,每个结点维护子