hdu4417Super Mario

这题是求一段区间中小于等于P的数的个数。。

这题可以用离线线段树写

将所有的询问离线读入之后,按H从小到大排序。然后对于所有的结点也按从小到大排序,然后根据查询的H,将比H小的点加入到线段树,然后就是一个区间和。

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 100050
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
int sum[MAXN<<2];
int ans[MAXN];
int N,M;
struct node{
    int pos,val;
    bool operator <(const node &b)const{
        return val<b.val;
    }
}nn[MAXN];
struct eee{
    int l,r,h,pos;
    bool operator <(const eee &b)const{
        return h<b.h;
    }
}ee[MAXN];
void pushup(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt){
    if(l==r){
        sum[rt]=0;
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(rt);
}
void update(int p,int val,int l,int r,int rt){
    if(l==r&&l==p){
        sum[rt]+=val;
        return;
    }
    int m=(l+r)>>1;
    if(p<=m)update(p,val,lson);
    else update(p,val,rson);
    pushup(rt);
}
int query(int L,int R,int l,int r,int rt){
    if(L<=l&&R>=r){
        return sum[rt];
    }
    int m=(l+r)>>1;
    int re=0;
    if(L<=m)re+=query(L,R,lson);
    if(R>m)re+=query(L,R,rson);
    return re;
}
int main(){
    int tt;
    scanf("%d",&tt);
    for(int cas=1;cas<=tt;cas++){
        memset(ans,0,sizeof(ans));
        scanf("%d%d",&N,&M);
        for(int i=0;i<N;i++){
            scanf("%d",&nn[i].val);
            nn[i].pos=i+1;
        }
        for(int i=0;i<M;i++){
            scanf("%d%d%d",&ee[i].l,&ee[i].r,&ee[i].h);
            ee[i].l++,ee[i].r++;
            ee[i].pos=i;
        }
        sort(nn,nn+N);
        sort(ee,ee+M);
        build(1,N,1);
        int k=0;
        for(int i=0;i<M;i++){
            while(k<N&&nn[k].val<=ee[i].h){
                update(nn[k].pos,1,1,N,1);
                k++;
            }
            ans[ee[i].pos]=query(ee[i].l,ee[i].r,1,N,1);
        }
        printf("Case %d:\n",cas);
        for(int i=0;i<M;i++)
        printf("%d\n",ans[i]);
    }
    return 0;
}

题解是二分划分树求第K小值与P比较,然后就可以得到小于等于P的数的个数

http://acm.hdu.edu.cn/showproblem.php?pid=4417

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 100050
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
int sorted[MAXN];
int toLeft[20][MAXN];//20代表的是20层
int val[20][MAXN];
void build(int d,int l,int r,int rt){
    if(l==r)return;
    int mid=(l+r)>>1;
    int lsame=mid-l+1;//lsame表示和val_mid相等且分到左边的
    for(int i=l;i<=r;i++){
        if(val[d][i]<sorted[mid]){
            lsame--;//先假设左边的数(mid - l + 1)个都等于val_mid,然后把实际上小于val_mid的减去
        }
    }
    int lpos=l;
    int rpos=mid+1;
    int same=0;
    for(int i=l;i<=r;i++){
        if(i==l){
            toLeft[d][i]=0;//toLeft[i]表示[ l , i ]区域里有多少个数分到左边
        }
        else{
            toLeft[d][i]=toLeft[d][i-1];
        }
        if(val[d][i]<sorted[mid]){
            toLeft[d][i]++;
            val[d+1][lpos++]=val[d][i];
        }
        else if(val[d][i]>sorted[mid]){
            val[d+1][rpos++]=val[d][i];
        }
        else{
            if(same<lsame){//有lsame的数是分到左边的
                same++;
                toLeft[d][i]++;
                val[d+1][lpos++]=val[d][i];
            }
            else{
                val[d+1][rpos++]=val[d][i];
            }
        }
    }
        build(d+1,lson);
        build(d+1,rson);
}
int query(int L,int R,int k,int d,int l,int r,int rt){
    if(l==r){
        return val[d][l];
    }
    int s;//s表示[ L , R ]有多少个分到左边
    int ss;//ss表示 [l , L-1 ]有多少个分到左边
    int mid=(l+r)>>1;
    if(L==l){
        s=toLeft[d][R];
        ss=0;
    }
    else{
        s=toLeft[d][R]-toLeft[d][L-1];
        ss=toLeft[d][L-1];
    }
    if(s>=k){//有多于k个分到左边,显然去左儿子区间找第k个
        int newl=l+ss;
        int newr=l+ss+s-1;//计算出新的映射区间
        return query(newl,newr,k,d+1,lson);
    }
    else {
        int bb=L-l-ss;//bb表示 [l , L-1 ]有多少个分到右边
        int b=R-L-s+1;//b表示 [L , R]有多少个分到右边
        int newl=mid+bb+1;
        int newr=mid+bb+b;
        return query(newl,newr,k-s,d+1,rson);
    }
}
int main()
{
    freopen("in.txt","r",stdin);
    int cas=1;
    int T;
    scanf("%d",&T);
    while(T--){
    printf("Case %d:\n",cas++);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&val[0][i]);
        sorted[i]=val[0][i];
    }
    sort(sorted+1,sorted+n+1);
    build(0,1,n,1);
    while(m--){
        int l,r,p;
        scanf("%d%d%d",&l,&r,&p);
    //因为这个划分数模版的下标是1-n,而这题是0~n-1,故需要将l++,r++
        l++,r++;
        int  ll=1,rr=(r-l+1);
        int a;

        while(ll<=rr){
            int mid=ll+((rr-ll)>>1);
            a=query(l,r,mid,0,1,n,1);
            if(a>p)rr=mid-1;
            else ll=mid+1;
        }
            printf("%d\n",rr);
    }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/arbitrary/p/2699862.html