zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    Asp.Net MVC3 简单入门第一季(五) 通过Asp.Net MVC的区域功能实现将多个MVC项目部署到一个站点
    Asp.Net MVC3 简单入门第一季(一)环境准备
    Asp.Net MVC3 简单入门第一季(三)详解Controller之Filter
    Entity Framework快速入门一对零到一关系处理
    Html5 学习系列(一)认识HTML5
    程序员体检完事后的感受
    Asp.Net MVC3 简单入门第一季 【索引贴】
    实在忍不住,【转】一篇好文章!题目:【转】想要成功,请记住!
    Html5 学习系列(二)HTML5新增结构标签
    大学应该怎么读?
  • 原文地址:https://www.cnblogs.com/arbitrary/p/2699862.html
Copyright © 2011-2022 走看看