POJ 2104 K-th Number 可持久化线段树

来源:互联网 发布:自干五 知乎 编辑:程序博客网 时间:2024/06/04 17:50

K-th Number

Time Limit: 20000MS Memory Limit: 65536K
Total Submissions: 58315 Accepted: 20211
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.

题意:求区间第K大
题解:可持久化线段树的裸题。我居然过了这么久才来搞这个东西。果然还是太弱了。虽然这不是我第一次做可持久化线段树,但这是第一篇这个的博客qwq(之前打的那个风格有点奇怪,而且也不知道对不对)
可持久化线段树其实可以通俗地认为是线段树+前缀和。建的是一颗值域线段树,所以满足区间的加减性,再加上它的每一棵树与前一棵树有很多的节点是相同的(其实每次只修改了一个点),如果建多颗线段树的时候可能会MLE,所以我们可以利用已经建过的树的信息,直接在原来的树的基础上加入新的信息。同时存储新的根。
例如这道题:如果求整个区间的第k大值,是很容易用二分+值域线段树查到的,如果求区间的话怎么办?那就直接对每一个节点都开一个线段树,存储区间[1,i]的信息,那么求区间[l,r]就可以通过查询[1,r]-[1,l-1]来实现。MLE?对每次新的节点,直接在原来的树上加入就行了。再维护各个点的root。

#include<cstdio>#include<cstring>#include<algorithm>#include<map>using namespace std;const int N = 500010;map <int,int> q;int n,m;int a[N],b[N];int tail=0;void lisanhua(){    sort(b+1,b+n+1);    int cnt=unique(b+1,b+n+1)-b-1;    for(register int i=1;i<=n;i++){       int x=a[i];       a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;       q[a[i]]=x;    }}struct node{    int sum;    int l,r;    int ls,rs;}t[N<<2];int root[N];void update(int root){    t[root].sum=t[t[root].ls].sum+t[t[root].rs].sum;}void build(int &root,int l,int r){    root=++tail;    t[root].l=l,t[root].r=r;    if(l==r) return ;    int mid=l+r>>1;    build(t[root].ls,l,mid);    build(t[root].rs,mid+1,r);}void modify(int &root,int pos,int val){    root=++tail;    t[root]=t[pos];    int l=t[root].l,r=t[root].r;    if(val==l&&l==r) {t[root].sum++;return ;}    int mid=l+r>>1;    if(val<=mid) modify(t[root].ls,t[pos].ls,val);    else modify(t[root].rs,t[pos].rs,val);    update(root);}int query(int root1,int root2,int k){    int l=t[root1].l,r=t[root1].r;    if(l==r) return l;    int sum=t[t[root2].ls].sum-t[t[root1].ls].sum;    if(sum>=k) return query(t[root1].ls,t[root2].ls,k);    else return query(t[root1].rs,t[root2].rs,k-sum);}int main(){    scanf("%d%d",&n,&m);    for(register int i=1;i<=n;i++) {scanf("%d",&a[i]);b[i]=a[i];}    lisanhua();    build(root[0],1,n);    for(register int i=1;i<=n;i++) modify(root[i],root[i-1],a[i]);    while(m--){        int l,r,k;        scanf("%d%d%d",&l,&r,&k);        printf("%d\n",q[query(root[l-1],root[r],k)]);    }    return 0;}