poj2104

链接:点击打开链接

题意:给定一个数列a1,a2,...,an和m个三元组表示的查询。对于每个查询(i,j,k),输出ai,...,aj的升序排列中的第k个数

代码1:

#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int SIZE=100005;
int num[SIZE];
vector<int> tree[SIZE<<2];
void build(int l,int r,int rt){
    int m;
    if(l==r){
        tree[rt].push_back(num[l]);
        return ;
    }
    m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    tree[rt].resize(r-l+1);
    merge(tree[rt<<1].begin(),tree[rt<<1].end(),tree[rt<<1|1].begin(),tree[rt<<1|1].end(),
          tree[rt].begin());
}                                                   //每个节点由线段数的一个值,变为一个数列
int query(int L,int R,int x,int l,int r,int rt){
    int m,ans;
    if(L<=l&&r<=R)
    return upper_bound(tree[rt].begin(),tree[rt].end(),x)-tree[rt].begin();
    ans=0;
    m=(l+r)>>1;
    if(R<=m)
    ans+=query(L,R,x,l,m,rt<<1);
    else if(L>m)
    ans+=query(L,R,x,m+1,r,rt<<1|1);
    return ans;
}                                                   //其余同线段树基本是一样的
int main(){
    int a,b,c,i,j,l,r,ans,mid,tmp;
    while(scanf("%d%d",&n,&m)!=EOF){                //求第k个数,这题时间比较宽裕,因此可以用归并树加二分,但最好的方法依然是
        for(i=1;i<=n;i++)                           //划分树
        scanf("%d",&num[i]);
        build(1,n,1);
        sort(num+1,num+n+1);
        while(m--){
            scanf("%d%d%d",&a,&b,&c);
            l=1,r=n;
            while(l<=r){                            //二分找第k个数
                mid=(l+r)>>1;
                tmp=query(a,b,num[mid],1,n,1);
                if(tmp>=c){
                    ans=mid;
                    r=mid-1;
                }
                else
                l=mid+1;
            }
            printf("%d\n",num[ans]);
        }
    }
    return 0;
}

代码2:

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int SIZE=100005;
int tmp[SIZE],toleft[50][SIZE];             //toleft[i][j]为第i层前j个值有多少个进入左树
int tree[50][SIZE];                         //为划分树,每一行代表树的一层
void build(int l,int r,int rt){
    int i,mid,sum,lpos,rpos;
    if(l==r)
    return;
    mid=(l+r)>>1;
    sum=mid-l+1;
    for(i=l;i<=r;i++)                       //划分树就是一颗依照快排的思想,从上而下建的树
    if(tree[rt][i]<tmp[mid])                //因此每一个节点的左儿子中不可能存在比右儿子中
    sum--;                                  //大的数,依照这个原则建树
    lpos=l,rpos=mid+1;
    for(i=l;i<=r;i++){
        if(tree[rt][i]<tmp[mid])
        tree[rt+1][lpos++]=tree[rt][i];
        else if(tree[rt][i]==tmp[mid]&&sum>0){
            tree[rt+1][lpos++]=tree[rt][i];
            sum--;
        }
        else
        tree[rt+1][rpos++]=tree[rt][i];
        toleft[rt][i]=toleft[rt][l-1]+lpos-l;
    }
    build(l,mid,rt+1);
    build(mid+1,r,rt+1);
}
int query(int L,int R,int k,int l,int r,int rt){
    int mid,newl,newr,cnt;                 //询问的时候如果区间内进入左树的数量大于
    if(L==R)                               //k,那么第k个值一定在左树,否则则相当于在
    return tree[rt][L];                    //右树中找第k-cnt个值(cnt为当前区间进入左
    mid=(l+r)>>1;                          //树的个数),按照这个原则将区间逐渐缩小
    cnt=toleft[rt][R]-toleft[rt][L-1];
    if(cnt>=k){
        newl=l+toleft[rt][L-1]-toleft[rt][l-1];
        newr=newl+cnt-1;                   //先出现的数一定会先进入左树或者右树
        return query(newl,newr,k,l,mid,rt+1);
    }
    else{
        newr=R+toleft[rt][r]-toleft[rt][R];
        newl=newr-(R-L-cnt);
        return query(newl,newr,k-cnt,mid+1,r,rt+1);
    }
}
int main(){
    int n,m,i,a,b,c;
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(tree,0,sizeof(tree));
        for(i=1;i<=n;i++){
            scanf("%d",&tree[0][i]);
            tmp[i]=tree[0][i];
        }
        sort(tmp+1,tmp+n+1);
        build(1,n,0);
        while(m--){
            scanf("%d%d%d",&a,&b,&c);
            printf("%d\n",query(a,b,c,1,n,0));
        }
    }
    return 0;
}



 

你可能感兴趣的:(poj2104)