HDU 4417 Super Mario 树状数组+离线处理

 

样例输入:

1
10 10
5 3 6 2 1 0 8 6 3 4 
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
样例输出:

Case 1:
6
1
0
6
0
3
1
2
5
1
题目大意:

        T组输入,每次给定n个不同的高度,q次询问,每次询问输出询问区间高度小于等于询问高度的个数。

解题思路:

        题目初看很简单,但是每次遍历必定会T,这个时候,我们就需要用到树状数组了,按照树状数组求逆序对的方法,进行从小到大插入到相应位置一个1,在此之前,我们肯定要把输入的一串数字从小到大排序,同时还需记住他们原来的位置,接着由于是q次询问,我们如果询问一次,就重新从小到大更新树状数组,又显得很繁琐,而且也可能会T,这个时候,我们就需要离线操作,将询问按询问高度排个序,这样就不用重复操作了,我们从第一个询问开始遍历,这个时候就有两种情况,如果前一次询问高度与当前询问高度不同,我们就需要遍历给定数组,在数组元素不超过询问高度的前提下,进行树状数组插入操作,即在元素对应位置插入1,这样就满足,计算的区间内,比询问高度高的还未插入,数组位置为0,而小于等于询问高度的地方插入为1,区间和就是小于等于询问高度的个数;这是一种情况,还有一种情况就是前一次询问高度与当前询问高度相同,这个时候显然我们就不需要更新树状数组,直接计算答案就行了。计算好答案,再按询问次序排列输出答案就行了,同时,此题还有个坑就是它位置是从0开始计算的,所以输入的询问区间,让右边界+1,左边界不用+1,因为计算区间和本身就要让左边界-1。

AC代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
using LL=long long;
#define N (int)1e5+5
int n,m;
int tree[2*N];
struct node{//存储给定数组
    int i;
    LL val;
}pos[N];
struct _node{//存储询问信息
    int l,r;
    int idx;
    LL h;
    int ans;
}q[N];
bool cmp(struct node a,struct node b)
{
    return a.val<b.val;
}
bool cmp1(struct _node a,struct _node b)
{
    return a.h<b.h;
}
bool cmp2(struct _node a,struct _node b)
{
    return a.idx<b.idx;
}
int lowbit(int x)
{
    return x&-x;
}
void update(int idx)
{
    while(idx<=n)
    {
        tree[idx]++;
        idx+=lowbit(idx);
    }
}
int query(int idx)
{
    int res=0;
    while(idx)
    {
        res+=tree[idx];
        idx-=lowbit(idx);
    }
    return res;
}
int main()
{
    int t;
    scanf("%d",&t);
    for (int z=1; z<=t; z++) {
        scanf("%d%d",&n,&m);
        for (int i=1; i<=n; i++) {
            scanf("%lld",&pos[i].val);
            pos[i].i=i;//存储位置
        }
        sort(pos+1, pos+n+1, cmp);//给定数组按从小到大排序
        for (int i=1; i<=m; i++) {
            scanf("%d%d%lld",&q[i].l,&q[i].r,&q[i].h);
            q[i].r++;//右边界+1
            q[i].idx=i;//记录询问次序
        }
        sort(q+1, q+m+1, cmp1);//按询问高度排序
        q[0].h=-1;//初始化q[0].h为-1,防止q[1].h为0导致没更新树状数组,影响计算
        for (int i=1,j=1; i<=m; i++) {
            if(q[i].h!=q[i-1].h)
            {
                while(pos[j].val<=q[i].h&&j<=n)//当前询问高度不等于前一次询问高度时,更新树状数组
                {
                    update(pos[j].i);
                    j++;
                }
            }
            q[i].ans=query(q[i].r)-query(q[i].l);//计算答案,l由于输入时没+1,所以这里不用-1
        }
        sort(q+1, q+m+1, cmp2);//按询问次序排序
        printf("Case %d:\n",z);
        for (int i=1; i<=m; i++) {
            printf("%d\n",q[i].ans);
        }
        memset(tree, 0, sizeof(tree));//树状数组归零
    }
    return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值