HDU 4417 超级马里奥 数据结构+利用树状数组进行快速统计+多维统计转换

HDU 4417 超级马里奥

Time Limit:1000MS    Memory Limit:32768KB    64bit IO Format:%I64d & %I64u

Description

马里奥是一个举世闻名的管道工,他的跳跃能力让我们钦佩。在一条长度为n的道路上,在每个整数点i的位置都有一个高度为hi的障碍物。现在的问题是:假设马里奥可以跳跃的最高高度为H,在道路的[L,R] 区间内他可以跳跃过的障碍物有多少个(不要考虑他被挡住)?

Input

第一行是数据个数T。
对于每组数据:
第一行包含两个整数n, m (1 <= n <=10^5, 1 <= m <= 10^5), n 表示道路的程度, m是询问的个数.
第二行包含n个整数,表示每个障碍物的高度, 高度范围是 [0, 1000000000].
接着 m 行,每行3个整数 L, R,H.( 0 <= L <= R < n 0 <= H <= 1000000000.)

Output

对于每组数据, 先输出"Case X: " ( X表示组数) 然后有m行, 每行包含一个整数. 第i个整数表示第i个询问的答案。

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 

解题思路:
这题是一个利用树状数组或线段树进行快速统计的一个题,题目不难但是不是很好想起来
比赛的时候没有想起来,简直被自己蠢哭了,唉。
1,首先这是一个经典的统计问题,求区间内小于某个数的个数有多少
2,这是一个二维统计问题,有两个变量需要考虑一个是区间,另一个是高度
3,因为区间不好通过排序消除影响,所以就是通过高度的排序消除高度的影响了
4,把询问的高度按照从小到大排序,顺序取出每一个高度h,然后把这条路上的所有的小于
这个高度的填充进(树状数组或线段树)对应的下标那里加一,然后对于h的查询就很好算了
5,简单统计一定要想到这种做法。。。。
下面代码用的树状数组。


#include<cstdio>
#include<algorithm>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 100005;
int n ;
struct node{
    int id;
    int x ;
    int y ;
    int h ;
    int num;
};
node arry[maxn] ;
int C[maxn] ;
struct node2{
    int id;
    int h ;
};
node2 s[maxn] ;
int lowbit(int x){return x&-x;}
int sum(int x){
    int ret = 0 ;
    while(x>0){
        ret+=C[x];
        x-=lowbit(x);
        //printf("shgfaj");
        //printf("ret = %d\n",ret);
    }
    //printf("ret = %d\n",ret);
    return ret ;
}
void add(int x,int d){
    while(x<=n){
        C[x]+=d ;
        x+=lowbit(x) ;
        //printf("asd");
    }
    return ;
}


bool cmp(node a,node b){
    return a.h<b.h ;
}
bool cmp2(node a,node b){
    return a.id<b.id ;
}
bool cmp3(node2 a,node2 b){
    return a.h<b.h ;
}


int main() {
    int m;
    int T ,cas=1;
    //freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&s[i].h);
            s[i].id = i ;
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&arry[i].x,&arry[i].y,&arry[i].h);
            arry[i].x++ ;
            arry[i].y++ ;
            arry[i].id = i ;
        }
        sort(arry+1,arry+m+1,cmp) ;
        sort(s+1,s+n+1,cmp3);


//        for(int i=1;i<=m;i++){
//            printf("%d ",arry[i].h);
//        }printf("\n");
//        for(int i=1;i<=m;i++){
//            printf("%d ",s[i].h);
//        }printf("\n");


        memset(C,0,sizeof(C));
        int j=1;
        for(int i=1;i<=m;i++){
            for(;j<=n&&s[j].h<=arry[i].h;j++){
                add(s[j].id,1);
                //printf("id = %d\n",s[j].id);
            }
            //printf("i = %d\n",i);
            int as = sum(arry[i].y) ;
            //printf("sssss = %d\n",as);
            arry[i].num = sum(arry[i].y)-sum(arry[i].x-1) ;

        }
        //printf("max = %d\n",sum(n));
        sort(arry+1,arry+m+1,cmp2) ;
        printf("Case %d:\n",cas++);
        for(int i=1;i<=m;i++){
            printf("%d\n",arry[i].num);
        }

    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值