poj 2761 多种数据结构算法求区间第k大的数

来源:互联网 发布:八零网络验证 编辑:程序博客网 时间:2024/05/17 09:39
Feed the dogs
Time Limit: 6000MS Memory Limit: 65536KTotal Submissions: 15517 Accepted: 4793

Description

Wind loves pretty dogs very much, and she has n pet dogs. So Jiajia has to feed the dogs every day for Wind. Jiajia loves Wind, but not the dogs, so Jiajia use a special way to feed the dogs. At lunchtime, the dogs will stand on one line, numbered from 1 to n, the leftmost one is 1, the second one is 2, and so on. In each feeding, Jiajia choose an inteval[i,j], select the k-th pretty dog to feed. Of course Jiajia has his own way of deciding the pretty value of each dog. It should be noted that Jiajia do not want to feed any position too much, because it may cause some death of dogs. If so, Wind will be angry and the aftereffect will be serious. Hence any feeding inteval will not contain another completely, though the intervals may intersect with each other. 

Your task is to help Jiajia calculate which dog ate the food after each feeding. 

Input

The first line contains n and m, indicates the number of dogs and the number of feedings. 

The second line contains n integers, describe the pretty value of each dog from left to right. You should notice that the dog with lower pretty value is prettier. 

Each of following m lines contain three integer i,j,k, it means that Jiajia feed the k-th pretty dog in this feeding. 

You can assume that n<100001 and m<50001. 

Output

Output file has m lines. The i-th line should contain the pretty value of the dog who got the food in the i-th feeding.

Sample Input

7 21 5 2 6 3 7 41 5 32 7 1

Sample Output

32

Source

POJ Monthly--2006.02.26,zgl & twb
主席树做法
22108K2266MSC++1406B
#include<stdio.h>#include<algorithm>#include<iostream>#define N 100005#define M N*20using namespace std;struct node{    int x,y,sum;}a[M];int b[N],t[N],root[N],num;void update(int k,int &c,int x,int y){    a[num++]=a[c];  c=num-1;    ++a[c].sum;    if(x==y)  return;    int mid=(x+y)>>1;    if(k<=mid)        update(k,a[c].x,x,mid);    else        update(k,a[c].y,mid+1,y);}int query(int i,int j,int k,int x,int y){    if(x==y)  return x;    int p=a[a[j].x].sum-a[a[i].x].sum;    int mid=(x+y)>>1;    if(k<=p)        return query(a[i].x,a[j].x,k,x,mid);    else        return query(a[i].y,a[j].y,k-p,mid+1,y);}int main(){    int n,m,i,j,k,q,pos;    while(scanf("%d%d",&n,&q)!=EOF)    {        for(i=1;i<=n;i++)        {            scanf("%d",&b[i]);            t[i]=b[i];        }        sort(t+1,t+1+n);        m=unique(t+1,t+1+n)-t-1;//去重        a[0].x=a[0].y=a[0].sum=0;        root[0]=0;        num=1;        for(i=1;i<=n;i++)        {            pos=lower_bound(t+1,t+1+m,b[i])-t;//找见位置            root[i]=root[i-1];            //printf("i=%d pos=%d root[i]=%d\n",i,pos,root[i]);            update(pos,root[i],1,m);        }        while(q--)        {            scanf("%d%d%d",&i,&j,&k);            printf("%d\n",t[query(root[i-1],root[j],k,1,m)]);        }    }    return 0;}

线段树做法:
6952K2375MSC++2103B
/*6952K2375MSC++2103B题意:给出每只狗的pretty value,然后多次询问,每次输出区间[i,j](狗站成一排,从第i只到第j只)的第k小的值是多少。注意:区间之间有交叉,但是没有完全包含。分析:先把所有区间排序,然后从左至右把每个区间用线段树维护离散化后的pretty value,即线段树的区间的意义是pretty value。每次删除在上一个区间中且不在当前区间中的节点,插入在当前区间中且不在上一个区间中的节点,使得线段树中的节点恰好为该区间内的所有节点。然后查询第k个就容易了。*/#include<stdio.h>#include<algorithm>#define N 100005using namespace std;int rank[N],ans[N];struct pp{    int l,r,id,k,s;}b[N],c[N];struct node{    int x,y,len;}a[N*3];bool cmp1(pp a,pp b){    return a.l<b.l;}bool cmp2(pp a,pp b){    return a.id<b.id;}int max(int a,int b){    return a>b?a:b;}int min(int a,int b){    return a<b?a:b;}void build(int t,int x,int y){    a[t].x=x; a[t].y=y; a[t].len=0;    if(x==y)  return ;    int mid=(x+y)>>1,temp=t<<1;    build(temp,x,mid);    build(temp+1,mid+1,y);}void update(int t,int k,int val){    if(a[t].x==a[t].y)    {        a[t].len+=val;        return;    }    int mid=(a[t].x+a[t].y)>>1,temp=t<<1;    if(k<=mid)        update(temp,k,val);    else        update(temp+1,k,val);    a[t].len=a[temp].len+a[temp+1].len;}int query(int t,int k){    if(a[t].x==a[t].y)        return ans[a[t].x];    int mid=(a[t].x+a[t].y)>>1,temp=t<<1;    if(a[temp].len>=k)        return query(temp,k);    else        return query(temp+1,k-a[temp].len);}int main(){    int n,m,i,j;    while(scanf("%d%d",&n,&m)!=EOF)    {        for(i=1;i<=n;i++)        {            scanf("%d",&c[i].l);            c[i].id=i;        }        sort(c+1,c+1+n,cmp1);        for(i=1;i<=n;i++)        {            rank[c[i].id]=i;            ans[i]=c[i].l;        }        for(i=1;i<=m;i++)        {            scanf("%d%d%d",&b[i].l,&b[i].r,&b[i].k);            b[i].id=i;        }        sort(b+1,b+1+m,cmp1);        build(1,1,n);        for(j=b[1].l;j<=b[1].r;j++)            update(1,rank[j],1);        b[1].s=query(1,b[1].k);        for(i=2;i<=m;i++)        {            for(j=b[i-1].l;j<=min(b[i-1].r,b[i].l-1);j++)//注意区间的范围               update(1,rank[j],-1);            for(j=max(b[i-1].r+1,b[i].l);j<=b[i].r;j++)//注意区间的范围                update(1,rank[j],1);            b[i].s=query(1,b[i].k);        }        sort(b+1,b+1+m,cmp2);        for(i=1;i<=m;i++)            printf("%d\n",b[i].s);    }    return 0;}
树状数组做法:
4288K1969MSC++1937B
/*4288K1969MSC++1937B*/#include<stdio.h>#include<string.h>#include<algorithm>#define N 100005using namespace std;struct node{    int x,y,id,k,s;}a[N],b[N];int ans[N],rank[N],c[N];bool cmp(node a,node b){    return a.x<b.x;}bool cmp1(node a,node b){    return a.id<b.id;}int max(int a,int b){return a>b?a:b;}int min(int a,int b){return a<b?a:b;}int lowbix(int x){    return x&(-x);}void update(int x,int num){    while(x<=N)    {        c[x]+=num;        x+=lowbix(x);    }}int getsum(int x){    int sum=0;    while(x)    {        sum+=c[x];        x-=lowbix(x);    }    return sum;}int binary(int k){    int l,r,cnt,an;    l=1;r=N;    while(l<=r)    {        int mid=(l+r)>>1;        an=getsum(mid);        if(an>=k)        {            r=mid-1;            cnt=mid;        }        else            l=mid+1;    }    return ans[cnt];}int main(){    int n,m,i,j;    while(scanf("%d%d",&n,&m)!=EOF)    {        for(i=1;i<=n;i++)        {            scanf("%d",&a[i].x);            a[i].id=i;        }        sort(a+1,a+1+n,cmp);        for(i=1;i<=n;i++)        {            rank[a[i].id]=i;            ans[i]=a[i].x;        }        for(i=1;i<=m;i++)        {            scanf("%d%d%d",&b[i].x,&b[i].y,&b[i].k);            b[i].id=i;        }        sort(b+1,b+1+m,cmp);        memset(c,0,sizeof(c));        for(i=b[1].x;i<=b[1].y;i++)            update(rank[i],1);        b[1].s=binary(b[1].k);        for(i=2;i<=m;i++)        {            for(j=b[i-1].x;j<=min(b[i-1].y,b[i].x-1);j++)                update(rank[j],-1);            for(j=max(b[i-1].y+1,b[i].x);j<=b[i].y;j++)                update(rank[j],1);            b[i].s=binary(b[i].k);        }        sort(b+1,b+1+m,cmp1);          for(i=1;i<=m;i++)              printf("%d\n",b[i].s);    }    return 0;}

划分树做法:
25992K2157MSC++2779B
#include<stdio.h>#include<algorithm>#define N 100100using namespace std;struct node{    int left,right,mid;}a[N*4];struct Tree//tree是记录划分的每一层的结果{    int val;//当前点的值    int num;//区间起点到该点之间有多少个点被移动到了右子区间    int p;//是否被移动到了右子区间}tree[20][N];//注意2^20要大于Nint st[N];void build(int left,int right,int cen,int t){    int m;    Tree *last=tree[cen-1],*cur=tree[cen];    //last上一层,cur当前层    a[t].left=left;    a[t].right=right;    m=a[t].mid=(left+right)>>1;    int mid=st[m],sum=0,j,ll=left,rr=m+1;     //mid保存的时候区间[left,right]的中值,建树最重要的是处理好重复的中值要放的位置。    //当然,如果数字没有重复是很好做的。    for(j=m;j>=left;j--)    {        if(st[j]==mid)            sum++;        else            break;    }    //记录下这个区间的左子区间里面有多少个重复的中值。    //也就是重复的中值有多少个要摆到左子区间去。    for(j=left;j<=right;j++)    {        int v=last[j].val;        if(v==mid)        {            if(sum)//首先遇到的sum个重复中值摆到左边            {                cur[ll++].val=mid;                last[j].p=last[j].num=0;                sum--;            }            else            {                cur[rr++].val=mid;                last[j].p=last[j].num=1;            }        }        else if(v<mid)        {//小于中值的摆到左子区间,摆的时候维持原来的相对次序            cur[ll++].val=v;            last[j].p=last[j].num=0;        }        else        {            cur[rr++].val=v;            last[j].p=last[j].num=1;        }    }    for(j=left+1;j<=right;j++)    { //这样可以累计下从left开始到当前元素中有多少个被移到了右子树中去        last[j].num+=last[j-1].num;    }    if(left==right)        return;    int temp=t<<1;    build(left,m,cen+1,temp);    build(m+1,right,cen+1,temp+1);}int query(int left,int right,int k,int cen,int t){    int mid=a[t].mid;    Tree ll=tree[cen][left],rr=tree[cen][right];    if(a[t].left==a[t].right)        return ll.val;    int dif=(right-left+1)-(rr.num-ll.num+ll.p),temp=t<<1;      //dif记录的时候区间[left,right]有多少个去了左子区间,dif=总个数-去右子区间的个数    if(dif>=k)    {        return query(left-ll.num+ll.p,right-rr.num,k,cen+1,temp);         //更新在左子区间查询的是[left-ll.num+ll.p,right-rr.num]里面的第k大值        //(看前面有多少个到了右边)    }    else    {        return query(mid+ll.num+1-ll.p,mid+rr.num,k-dif,cen+1,temp+1);         //更新右子区间查询[mid+ll.num+1-ll.p,mid+rr.num]里面的第k-dif大值        //(看来了右边的有多少个,加上起点mid)    }}int main(){    int n,m,i,j,k;    while(scanf("%d%d",&n,&m)!=EOF)    {        for(i=1;i<=n;i++)        {            scanf("%d",&st[i]);            tree[0][i].val=st[i];        }        sort(st+1,st+1+n);        build(1,n,1,1);        while(m--)        {            scanf("%d%d%d",&i,&j,&k);            printf("%d\n",query(i,j,k,0,1));        }    }    return 0;}


0 0
原创粉丝点击