K-th Number(主席树)

K-th Number
Time Limit: 20000MS Memory Limit: 65536K
Total Submissions: 64473 Accepted: 22684
Case Time Limit: 2000MS
Description

You are working for Macrohard company in data structures department. After failing your previous task about key insertion you were asked to write a new data structure that would be able to return quickly k-th order statistics in the array segment.
That is, given an array a[1…n] of different integer numbers, your program must answer a series of questions Q(i, j, k) in the form: “What would be the k-th number in a[i…j] segment, if this segment was sorted?”
For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3). The segment a[2…5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6), the third number is 5, and therefore the answer to the question is 5.
Input

The first line of the input file contains n — the size of the array, and m — the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000).
The second line contains n different integer numbers not exceeding 109 by their absolute values — the array for which the answers should be given.
The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).
Output

For each question output the answer to it — the k-th number in sorted a[i…j] segment.
Sample Input

7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
Sample Output

5
6
3
Hint

This problem has huge input,so please use c-style input(scanf,printf),or you may got time limit exceed.

#include
#include
#include
#include
#include
#define maxn 100007
using namespace std;
int a[maxn];//用来存储这n个数
int rk[maxn];//离散化以后的下标
int root[maxn];//用来存储前缀和
int n,m;//n个数 m个区间
struct LNode//用来离散化
{
    int value;
    int id;
}node[maxn];
bool cmp(LNode a,LNode b)//从小到大排序
{
    return a.value < b.value;
}
struct Tree
{
    int l,r,sum;//sum 这里存的是区间的和 也就是权值
    Tree()
    {
        sum =0;
    }
}T[maxn * 40];
int cnt = 0;//这里是主席树的个数
/*
    l ,r 代表区间的范围
    x 代表着第x 颗线段树
    y 代表着第y颗线段树
    pos 是代表着所在的位置
*/
void update(int l,int r,int &x,int y,int pos)
{
    //这里是因为修改一个节点只会更新一条链的长度 所以这里先吧第y 颗线段树
    //先接过来 然后在修改呢logn 个节点
    T[++cnt] = T[y];
    //这里代表着l~r 区间内又多了一个节点
    T[cnt].sum++;
    x = cnt;//x 代表着前缀和
    if(l==r) return ;//到达叶子节点
    int mid = (l + r)/2;//进行二分查找
    if(mid >= pos)//肯定在其左区间
    {
        //在其左区间所以区间范围就是l ~mid
        //这里由于是前缀和的思想 所以这里传的是 T[x].l 和 T[y].l
        update(l,mid,T[x].l,T[y].l,pos);
    }
    else // 在其右区间
    {
        update(mid+1,r,T[x].r,T[y].r,pos);
    }
}
// l 和 r 是区间范围 x 是第 l-1颗线段树 y 代表的是 第 r 颗线段树 k代表着区间的第k大
int query(int l,int r,int x,int y,int k)
{
       if(l == r) return l;//到达叶子节点 就直接返回 其下标
       //这个num的 含义代表的是 区间x ~y 上左子树的的个数
       int num = T[T[y].l].sum - T[T[x].l].sum;
       int mid = (l + r)/2;
       if(num >=k)//呢么第k大一定是位于左区间
       {
            return query(l,mid,T[x].l,T[y].l,k);
       }
       else
            return query(mid +1,r,T[x].r,T[y].r,k - num);
       //这里k-num 是因为 k 是L ~R 区间的第k大 而现在的情况是 已经确定了 L~R 中第K大位于右区间 所以呢么这个数相对于右区间中是k - num 大
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        cnt = 0;
        memset(root,0,sizeof(root));
        for(int i = 1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            node[i].value = a[i];//离散化处理
            node[i].id = i;
        }
        sort(node +1,node + n + 1,cmp);
        memset(rk,0,sizeof(rk));
        for(int i =1;i<=n;i++)
        {
            rk[node[i].id] = i;//将id映射成下标
        }
        for(int i = 1;i<=n;i++)
        {
            update(1,n,root[i],root[i-1],rk[i]);
        }
        while(m--)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            if(l > r) swap(l,r);
            int ans = query(1,n,root[l-1],root[r],k);
            //由于ans 你得到的是一个下标 所以你需要在离散化回去
            printf("%d\n",node[ans].value);
        }
    }

    return 0;
}

你可能感兴趣的:(线段树)