• HDU 4417 Super Mario 第37届ACM/ICPC 杭州赛区网络赛第1008题 (划分树)


    Super Mario

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 120    Accepted Submission(s): 73


    Problem Description
    Mario is world-famous plumber. His “burly” figure and amazing jumping ability reminded in our memory. Now the poor princess is in trouble again and Mario needs to save his lover. We regard the road to the boss’s castle as a line (the length is n), on every integer point i there is a brick on height hi. Now the question is how many bricks in [L, R] Mario can hit if the maximal height he can jump is H.
     
    Input
    The first line follows an integer T, the number of test data.
    For each test data:
    The first line contains two integers n, m (1 <= n <=10^5, 1 <= m <= 10^5), n is the length of the road, m is the number of queries.
    Next line contains n integers, the height of each brick, the range is [0, 1000000000].
    Next m lines, each line contains three integers L, R,H.( 0 <= L <= R < n 0 <= H <= 1000000000.)
     
    Output
    For each case, output "Case X: " (X is the case number starting from 1) followed by m lines, each line contains an integer. The ith integer is the number of bricks Mario can hit for the ith query.
     
    Sample Input
    1 10 10 0 5 2 7 5 4 3 8 7 7 2 8 6 3 5 0 1 3 1 1 9 4 0 1 0 3 5 5 5 5 1 4 6 3 1 5 7 5 7 3
     
    Sample Output
    Case 1: 4 0 0 3 1 2 0 1 5 1
     
    Source
     
    Recommend
    liuyiding
     
     
    此题比赛的时候想得比较久。
    比赛是离线处理,用树状数组或者线段树做的。。。
    因为一开始用树状数组写错一个地方,死循环了,一直TLE。所以换线段树写了下,还是TLE。
    后来马上发现错误,改正过后马上AC了。速度还是比较快的。
    树状数组或者线段树的题解见下面:
     
    晚上回来,突然想到划分树了。其实只要用划分树二分枚举第K大,得出答案就可以的。。。。
    Orz,原来这么多人快速过了都是用划分树的。。。
    划分树写起来确实简单,而且不容易错。。。自己太弱了。。。
    求现场赛给力啊!!!
    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    
    const int MAXN=100010;
    int tree[30][MAXN];//表示每层每个位置的值
    int sorted[MAXN];//已经排序的数
    int toleft[30][MAXN];//toleft[p][i]表示第i层从1到i有多少个数分入左边
    
    void build(int l,int r,int dep)
    {
        if(l==r)return;
        int mid=(l+r)>>1;
        int same=mid-l+1;//表示等于中间值而且被分入左边的个数
        for(int i=l;i<=r;i++)
          if(tree[dep][i]<sorted[mid])
             same--;
        int lpos=l;
        int rpos=mid+1;
        for(int i=l;i<=r;i++)
        {
            if(tree[dep][i]<sorted[mid])//比中间的数小,分入左边
                 tree[dep+1][lpos++]=tree[dep][i];
            else if(tree[dep][i]==sorted[mid]&&same>0)
            {
                tree[dep+1][lpos++]=tree[dep][i];
                same--;
            }
            else  //比中间值大分入右边
                tree[dep+1][rpos++]=tree[dep][i];
            toleft[dep][i]=toleft[dep][l-1]+lpos-l;//从1到i放左边的个数
    
        }
        build(l,mid,dep+1);
        build(mid+1,r,dep+1);
    
    }
    
    
    //查询区间第k大的数,[L,R]是大区间,[l,r]是要查询的小区间
    int query(int L,int R,int l,int r,int dep,int k)
    {
        if(l==r)return tree[dep][l];
        int mid=(L+R)>>1;
        int cnt=toleft[dep][r]-toleft[dep][l-1];//[l,r]中位于左边的个数
        if(cnt>=k)
        {
            //L+要查询的区间前被放在左边的个数
            int newl=L+toleft[dep][l-1]-toleft[dep][L-1];
            //左端点加上查询区间会被放在左边的个数
            int newr=newl+cnt-1;
            return query(L,mid,newl,newr,dep+1,k);
        }
        else
        {
             int newr=r+toleft[dep][R]-toleft[dep][r];
             int newl=newr-(r-l-cnt);
             return query(mid+1,R,newl,newr,dep+1,k-cnt);
        }
    }
    int solve(int n,int s,int t,int h)
    {
        int ans=0;
        int l=1;
        int r=(t-s)+1;
        int mid;
        while(l<=r)
        {
            mid=(l+r)>>1;
            int temp=query(1,n,s,t,0,mid);
            if(temp<=h)
            {
                ans=mid;
                l=mid+1;
            }
            else r=mid-1;
        }
        return ans;
    }
    
    int main()
    {
        //freopen("in.txt","r",stdin);
        //freopen("out.txt","w",stdout);
        int T;
        int n,m;
        int s,t,h;
        scanf("%d",&T);
        int iCase=0;
        while(T--)
        {
            iCase++;
            scanf("%d%d",&n,&m);
            memset(tree,0,sizeof(tree));//这个必须
            for(int i=1;i<=n;i++)//从1开始
            {
                scanf("%d",&tree[0][i]);
                sorted[i]=tree[0][i];
            }
            sort(sorted+1,sorted+n+1);
            build(1,n,0);
            printf("Case %d:\n",iCase);
            while(m--)
            {
                scanf("%d%d%d",&s,&t,&h);
                s++;
                t++;
                printf("%d\n",solve(n,s,t,h));
            }
        }
        return 0;
    }
  • 相关阅读:
    总结几个面试题
    产生下一个排列数的算法
    所谓码农
    简记微软实习生面试
    二维数组作为函数的参数传递
    详细解说 STL 排序(Sort)
    copy()之绝版应用
    STL标准模板库(简介)
    访问控制和继承方式
    常用软件开发模型比较分析
  • 原文地址:https://www.cnblogs.com/kuangbin/p/2699178.html
Copyright © 2020-2023  润新知