zoukankan      html  css  js  c++  java
  • hdu4417 Super Mario (树状数组/分块/主席树)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417

    题目大意:给定一个长度为n的序列,有m个询问,每次询问包含l,r,h,即询问区间[l,r]小于等于h的数目。

    解题思路:很多种解法,写了三种

    1.树状数组离线处理

    将序列和所有操作的h从小到大排序,都设为结构体类型以便保存下标,然后按顺序从小到大将序列的下标更新到树状数组中,如果下一个询问的h要大于当前询问的h时,则处理该询问,因为我们已经将小于等于h的数全部更新到树状数组中,所以直接区间求和即为答案。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e5+7;
    int sum[maxn];
    int n,m;
    struct node{
        int h,id;
    }p[maxn];
    struct node1{
        int l,r,h,id;
    }q[maxn];
    int ans[maxn];
    bool cmp(node x,node y){
        return x.h<y.h;
    }
    bool cmp1(node1 x,node1 y){
        return x.h<y.h;
    }
    void update(int pos){
        while(pos<=n){
            sum[pos]++;
            pos+=(pos&-pos);
        }
    }
    int ask(int pos){
        int res=0;
        while(pos){
            res+=sum[pos];
            pos-=(pos&-pos);
        }
        return res;
    }
    int main(){
        int t;
        scanf("%d",&t);
        for(int tt=1;tt<=t;tt++){
            memset(sum,0,sizeof(sum));
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++){
                scanf("%d",&p[i].h);
                p[i].id=i;
            }
            for(int i=1;i<=m;i++){
                scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].h);
                q[i].l++; q[i].r++;
                q[i].id=i;
            }
            sort(p+1,p+1+n,cmp);
            sort(q+1,q+1+m,cmp1);
            for(int i=1,j=1;j<=m;j++){
                while(i<=n&&p[i].h<=q[j].h){ //将小于等于当前询问h的数的下标更新到树状数组中
                    update(p[i].id);
                    i++;
                }
                ans[q[j].id]=ask(q[j].r)-ask(q[j].l-1); //查找该答案
            }
            printf("Case %d:
    ",tt);
            for(int i=1;i<=m;i++){
                printf("%d
    ",ans[i]);
            }
        }
        return 0;
    }

    2.分块:

    将n个序列分成根号n块,用两个数组同时存序列,a数组为原序列,b数组为每块进行排序后的序列。对于询问操作,如果询问区间只在某块的部分区间的直接暴力,对于整块都在询问区间的块可以采用二分。不过这种方法耗时多一点。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e5+7;
    int n,m,a[maxn],b[maxn],num,block,l[maxn],r[maxn],belong[maxn];
    void build(){
        block=sqrt(n);
        num=n/block; if(n%block) num++;
        for(int i=1;i<=num;i++)
            l[i]=(i-1)*block+1,r[i]=i*block;
        r[num]=n;
        for(int i=1;i<=n;i++) belong[i]=(i-1)/block+1;
    }
    int ask(int x,int y,int val){
        int res=0;
        if(belong[x]==belong[y]){
            for(int i=x;i<=y;i++){
                if(a[i]<=val) res++;
            }
            return res;
        }
        for(int i=x;i<=r[belong[x]];i++){
            if(a[i]<=val) res++;
        }
        for(int i=l[belong[y]];i<=y;i++){
            if(a[i]<=val) res++;
        }
        for(int i=belong[x]+1;i<belong[y];i++){
            int pos=upper_bound(b+l[i],b+r[i]+1,val)-b-l[i];
            res+=pos;
        }
        return res;
    }
    int main(){
        int t;
        scanf("%d",&t);
        for(int tt=1;tt<=t;tt++){
            printf("Case %d:
    ",tt);
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i]);
                b[i]=a[i];
            }
            build();
            for(int i=1;i<=num;i++)
                sort(b+l[i],b+r[i]+1);
            while(m--){
                int x,y,h;
                scanf("%d%d%d",&x,&y,&h);
                x++; y++;
                printf("%d
    ",ask(x,y,h));
            }
        }
        return 0;
    }

    3.主席树

    将主席树的板子的询问操作稍微改改就可以了,对于询问操作,直接先二分查找大于h的第一个编号x,然后查找第l颗线段树到第r颗线段树中小于x编号的和就可以了。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e5+7;
    struct node{
        int l,r,sum;
    }tree[maxn*40];
    int n,m,cnt,a[maxn],root[maxn];
    vector<int> v;
    int getid(int x){
        return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
    }
    void update(int l,int r,int &x,int y,int pos){
        tree[++cnt]=tree[y],tree[cnt].sum++,x=cnt;
        if(l==r) return;
        int mid=(l+r)/2;
        if(pos<=mid) update(l,mid,tree[x].l,tree[y].l,pos);
        else update(mid+1,r,tree[x].r,tree[y].r,pos);
    }
    int query(int l,int r,int x,int y,int k){
        if(l==r) return tree[y].sum-tree[x].sum;
        int mid=(l+r)/2,ans=0;
        if(k<=mid) ans+=query(l,mid,tree[x].l,tree[y].l,k);
        else{
            ans+=tree[tree[y].l].sum-tree[tree[x].l].sum;
            ans+=query(mid+1,r,tree[x].r,tree[y].r,k);
        }
        return ans;
    }
    int main(){
        int T,tot=0;
        scanf("%d",&T);
        while(T--){
            v.clear();
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++) scanf("%d",&a[i]),v.push_back(a[i]);
            sort(v.begin(),v.end());
            v.erase(unique(v.begin(),v.end()),v.end());
            printf("Case %d:
    ",++tot);
            for(int i=1;i<=n;i++) update(1,n,root[i],root[i-1],getid(a[i]));
            while(m--){
                int l,r,h;
                scanf("%d%d%d",&l,&r,&h);
                l++; r++;
                int x=upper_bound(v.begin(),v.end(),h)-v.begin();
                if(x==0) printf("0
    ");
                else printf("%d
    ",query(1,n,root[l-1],root[r],x));
            }
        }
        return 0;
    }
  • 相关阅读:
    过滤选择器——简单过滤选择器
    层次选择器
    选择器——2——基本选择器
    This is a secret
    iframe加载问题
    懒加载
    biubiubiu
    使用JSON JavaScriptSerializer 进行序列化或反序列化时出错。字符串的长度超过了为 maxJsonLength属性
    HTML中&nbsp; &ensp; &emsp; &thinsp;等6种空白空格的区别
    ASP.NET 4.0的ClientIDMode属性
  • 原文地址:https://www.cnblogs.com/zjl192628928/p/11200370.html
Copyright © 2011-2022 走看看