SPOJ MKTHNUM & POJ 2104 - K-th Number - [主席树模板题]

题目链接:http://poj.org/problem?id=2104

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 10 9 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.

题意:

写一个数据结构,能够快速返回指定区间内的第k小元素。

即:数组a[1:n]包含n个不同的整数,Q(i,j,k)代表询问:“若a[i:j]中元素从小到大排列,则其中第k个元素是什么?”

例如:数组a = (1, 5, 2, 6, 3, 7, 4),现有查询Q(2, 5, 3),由于a[2:5]为(5, 2, 6, 3),升序排列后为(2, 3, 5, 6),第3个元素为5,所以Q(2, 5, 3) = 5

数据范围:

数组内元素个数n:1 ≤ n ≤ 100 000;

查询个数m:1 ≤ m ≤ 5 000;

元素大小:[-1e9,1e9];

对于询问Q(i,j,k):1 ≤ i ≤ j ≤ n, 1 ≤ k ≤ j - i + 1;

题解:

主席树参考:

  https://www.bilibili.com/video/av4619406/

  https://blog.csdn.net/regina8023/article/details/41910615

AC代码:

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=100000+5;

//主席树
struct Node{
    int l,r,sum;
}node[maxn*40];
int root[maxn];
int cnt;
void update(int l,int r,int x,int &y,int pos)
{
    cnt++;
    node[cnt]=node[x];
    node[cnt].sum++;
    y=cnt;

    if(l==r) return;

    int mid=(l+r)/2;
    if(mid>=pos) update(l,mid,node[x].l,node[y].l,pos);
    else update(mid+1,r,node[x].r,node[y].r,pos);
}
int query(int l,int r,int x,int y,int k)
{
    if(l==r) return l;

    int mid=(l+r)/2;
    int sum=node[node[y].l].sum-node[node[x].l].sum;
    if(sum>=k) return query(l,mid,node[x].l,node[y].l,k);
    else return query(mid+1,r,node[x].r,node[y].r,k-sum);
}

//离散化
vector<int> v;
inline int getID(int x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}
inline int getVal(int id){return v.at(id-1);}

int a[maxn];
int main()
{
    int n,q;
    scanf("%d%d",&n,&q);

    v.clear();
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        v.push_back(a[i]);
    }
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());

    root[0]=0; node[0].l=node[0].r=0; node[0].sum=0; //初始化第0棵树
    for(int i=1;i<=n;i++) update(1,n,root[i-1],root[i],getID(a[i])); //构建第1~n棵树

    for(int i=1;i<=q;i++)
    {
        int l,r,k;
        scanf("%d%d%d",&l,&r,&k);
        printf("%d\n",getVal(query(1,n,root[l-1],root[r],k)));
    }
}

注:

vector容器的方法:

  end()  指向迭代器中末端元素的下一个,指向一个不存在元素。
  erase(pos)  删除pos位置的数据,传回下一个数据的位置。
  erase(st,ed)  删除[st,ed)区间的数据,传回下一个数据的位置。

unique函数:

  unique(st,ed)  对[st,ed)区间的数据进行去重。

  功能:对有序的容器重新排列,将所有第一次出现的元素从前往后排,所有重复出现的元素依次排在后面。

  返回值:返回迭代器,迭代器指向的是重复元素的首地址。

lower_bound(st,ed,x)  在有序的序列中返回[st,ed)区间内第一个不小于x的元素的位置。

upper_bound(st,ed,x)  在有序的序列中返回[st,ed)区间内第一个大于x的元素的位置。

猜你喜欢

转载自www.cnblogs.com/dilthey/p/9339863.html