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 (n, m <= 100000), indicates the number of integers in the sequence and the number of the quaere.

The second line contains n integers, describe the sequence.

Each of following m lines contains three integers s, t, k.

[s, t] indicates the interval and k indicates the kth big number in interval [s, t]

Output

For each test case, output m lines. Each line contains the kth big number.

Sample Input

1
10 1
1 4 2 3 5 6 7 8 9 0
1 3 2 

Sample Output

2
/**
hdu 2665  可持久化线段树求区间第K大值(函数式线段树||主席树)
题目大意;给定一个区间,m个询问:指定区间的第k大的数
解题思路:所谓主席树,就是可持久化线段树,也就是说我们每插入了一个新的元素,就创造了一个新的结点,这样下去,
          线段树所有的历史版本我们就都能保存下来。然后考虑一下线段树相减,两棵线段树相减就是每一个结点相减,
          那么我们每一个结点更新一次,那么序列中每一个元素都对应了一个版本的线段树,也就是序列中所有的前缀的
          权值线段树,那么对于一个区间,通过前缀相减很快就能搞出来这个区间对应的线段树,然后询问这棵线段树的第K大值
    注:如果求区间第k小值转化为第(l-r-k+1)大值就可以了
*/
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;
const int N=100010;

int T[N];
int num[N];
int san[N];
int ls[N*20];
int rs[N*20];
int sum[N*20];
int tot,rt;
int n,m;

void build(int l,int r,int &rt)
{
    rt=++tot;
    sum[rt]=0;
    if(l==r)return;
    int m=(l+r)/2;
    build(l,m,ls[rt]);
    build(m+1,r,rs[rt]);
}

void update(int last,int p,int l,int r,int &rt)
{
    rt=++tot;
    ls[rt]=ls[last];
    rs[rt]=rs[last];
    sum[rt]=sum[last]+1;
    if(l==r)
        return;
    int m=(l+r)>>1;
    if(p<=m)
        update(ls[last],p,l,m,ls[rt]);
    else
        update(rs[last],p,m+1,r,rs[rt]);
}

int query(int ss,int tt,int l,int r,int k)
{
    if(l==r)return l;
    int m=(l+r)>>1;
    int cnt=sum[ls[tt]]-sum[ls[ss]];
    if(k<=cnt)
        return query(ls[ss],ls[tt],l,m,k);
    else
        return query(rs[ss],rs[tt],m+1,r,k-cnt);
}

int main()
{
    int tt;
    scanf("%d",&tt);
    while(tt--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&num[i]);
            san[i]=num[i];
        }
        tot=0;
        sort(san+1,san+n+1);
        int cnt=unique(san+1,san+n+1)-san-1;
        build(1,cnt,T[0]);
        for(int i=1; i<=n; i++)
        {
            num[i]=lower_bound(san+1,san+1+cnt,num[i])-san;
        }
        for(int i=1; i<=n; i++)update(T[i-1],num[i],1,cnt,T[i]);
        while(m--)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            int id=query(T[l-1],T[r],1,cnt,k);
            printf("%d\n",san[id]);
        }
    }
    return 0;
}
时间: 2024-10-12 17:03:27

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

poj 2401 划分树 求区间第k大的数

题目:http://poj.org/problem?id=2104 划分树待我好好理解下再写个教程吧,觉得网上的内容一般,,, 模板题: 贴代码: #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define CLR(a) memset(a,0,sizeof(a)) const int MAXN = 1000

poj 2104主席树求区间第k小

POJ - 2104 题意:求区间第k小 思路:无修改主席树 AC代码: #include "iostream" #include "iomanip" #include "string.h" #include "stack" #include "queue" #include "string" #include "vector" #include "set&

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

[csu/coj 1080]划分树求区间前k大数和

题意:从某个区间内最多选择k个数,使得和最大 思路:首先题目给定的数有负数,如果区间前k大出现负数,那么负数不选和更大,于是对于所有最优选择,负数不会出现,所以用0取代负数,问题便转化为区间的前k大数和. 划分树: [1  6  3  8  5  4  7  2] [6  8  5  7][1  3  4  2] [8  7][6  5][3  4][1  2] [8][7][6][5][4][3][2][1] 把快排的结果从上至下依次放入线段树,就构成了划分树,划分的意思就是选定一个数,把原序

POJ2761---Feed the dogs (Treap求区间第k大)

题意 就是求区间第k大,区间 不互相包含. 尝试用treap解决一下 第k大的问题. 1 #include <set> 2 #include <map> 3 #include <cmath> 4 #include <ctime> 5 #include <queue> 6 #include <stack> 7 #include <cstdio> 8 #include <string> 9 #include <

POJ 2761 Feed the dogs(树状数组求区间第K大)

题目链接: 戳我 题目大意:Jiajia要为宠物狗,宠物狗按成一排站好(1 < i <= n),第 i 只狗的喜欢程度是 a[i], 之后他会先喂某个区间内第k个 即 n 个数, m个询问,接着是 n个数 接下来 m 行,每行是 l r k即 l 到 r 这个区间第 k 小的数,每个询问输出一个答案,即 a[i] 求区间第k大有很多算法, 详见此博客 [数据结构练习] 求区间第K大数的几种方法 我用的树状数组解法,来自 树状数组从前往后求和,用来解第k大(或小)的数 poj 2985 The

主席树|求区间第k小模板

主席树 学了主席树,用来求区间上的第k小 写一下自己整理后的模板 求区间第k小 #include<bits/stdc++.h> using namespace std; //求区间第k小 const int maxn = 500010; struct node{ int v,lc,rc; }T[maxn * 21]; int n,m; int root[maxn]; int e; void insert(int pre,int cur,int pos,int l,int r){ if(l ==

POJ2104-- K-th Number(主席树静态区间第k大)

[转载]一篇还算可以的文章,关于可持久化线段树http://finaltheory.info/?p=249 无修改的区间第K大 我们先考虑简化的问题:我们要询问整个区间内的第K大.这样我们对值域建线段树,每个节点记录这个区间所包含的元素个数,建树和查询时的区间范围用递归参数传递,然后用二叉查找树的询问方式即可:即如果左边元素个数sum>=K,递归查找左子树第K大,否则递归查找右子树第K – sum大,直到返回叶子的值. 现在我们要回答对于区间[l, r]的第K大询问.如果我们能够得到一个插入原序

hdu 5412 CRB and Queries(线段树套笛卡尔树 - 动态区间第k大)

题目链接:hdu 5412 CRB and Queries 首先对所有出现过的值排序,建立线段树,每个线段树的节点是一棵笛卡尔树,笛卡尔树记录区间下标值. #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using namespace std; #define lson(x) (x<<1) #define rson(x) ((x<<