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

Feed the dogs
Time Limit: 6000MS Memory Limit: 65536K
Total 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 2
1 5 2 6 3 7 4
1 5 3
2 7 1

Sample Output

3
2

Source

主席树做法
22108K2266MSC++1406B
#include<stdio.h>
#include<algorithm>
#include<iostream>
#define N 100005
#define M N*20
using 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
/*6952K	2375MS	C++	2103B
题意:给出每只狗的pretty value,然后多次询问,每次输出区间[i,j]
(狗站成一排,从第i只到第j只)的第k小的值是多少。
注意:区间之间有交叉,但是没有完全包含。
分析:先把所有区间排序,然后从左至右把每个区间用线段树维护离散化后的pretty value,
即线段树的区间的意义是pretty value。每次删除在上一个区间中且不在当前区间中的节点,
插入在当前区间中且不在上一个区间中的节点,使得线段树中的节点恰好为该区间内的所有节点。
然后查询第k个就容易了。	
*/
#include<stdio.h>
#include<algorithm>
#define N 100005
using 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

/*4288K	1969MS	C++	1937B*/	
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100005
using 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 100100
using 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要大于N
int 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值