HDU - 4417 Super Mario(主席树、区间内小于等于h的数的个数)

题目链接

HDU - 4417 Super Mario

题目

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

题意

给定长度为n的数组a。然后m次查询,每次给定一个区间[l,r](下标从0开始)和h。求在区间[l,r]内小于等于h的数的个数。

分析

容易想到一个暴力解法,每次对区间去暴力计数,时间复杂度为O(m*k),k=(r-l+1)。显然会TLE。

考虑用“前缀和”的思想去优化。主席树的本质是对每一个前缀建一颗线段树,所以不难联想到用主席树去维护。

线段树中每一个结点代表一个区间,我们维护结点信息sum,sum表示这个a数组在该区间上的值的个数。这样,求[l,r](下标改为1后)中小于等于h的数的个数,可以通过查询第r个版本的线段树和第l-1个版本的线段树关于“在区间[l,r]内小于等于h的数的个数”的差值来解决。

AC代码

#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;

const int maxn=1e5+100;
vector<int> v;
int n,m,a[maxn],rt[maxn],tot;
struct node
{
    int l,r,sum;
}T[maxn*20];
int getid(int x)
{
    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void build(int &o,int l,int r)
{
    o=++tot;
    T[o].sum=0;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(T[o].l,l,mid);
    build(T[o].r,mid+1,r);
}
void update(int l,int r,int &now,int last,int k)
{
    T[++tot]=T[last];
    now=tot;
    T[now].sum++;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(k<=mid) update(l,mid,T[now].l,T[last].l,k);
    else update(mid+1,r,T[now].r,T[last].r,k);
}
int query(int l,int r,int x,int y,int k)
{
    if(l==r) return T[y].sum-T[x].sum;
    int mid=(l+r)>>1;
    if(k<=mid) return query(l,mid,T[x].l,T[y].l,k);
    else
    {
        int ret=0;
        ret+=T[T[y].l].sum-T[T[x].l].sum;
        ret+=query(mid+1,r,T[x].r,T[y].r,k);
        return ret;
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    for(int kase=1;kase<=t;kase++)
    {
        v.clear();
        tot=0;
        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());
        build(rt[0],1,n);
        for(int i=1;i<=n;i++)
            update(1,n,rt[i],rt[i-1],getid(a[i]));
        printf("Case %d:\n",kase);
        for(int i=1;i<=m;i++)
        {
            int l,r,h;
            scanf("%d%d%d",&l,&r,&h);
            l++,r++;
            int k=upper_bound(v.begin(),v.end(),h)-v.begin();//v中有k个元素小于等于h
            //upper的返回值是指向键值<=key的最后一个元素的后一个元素。 
            if(!k) printf("0\n");
            else
                printf("%d\n",query(1,n,rt[l-1],rt[r],k));
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37685156/article/details/80364092
今日推荐