hdu 4417 Super Mario(离线树状数组|划分树)

本文介绍了解决数列查询问题的两种高效算法,包括利用划分树和离线树状数组的方法,通过二分查找和排序优化,实现复杂查询的快速处理。算法的时间复杂度分别达到了 (O(n \log^2 n)) 和 (O(n \log n)),展示了在处理大量数据时的高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Super Mario

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


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   |   We have carefully selected several similar problems for you:   5041  5040  5039  5038  5037 
  题意:
给你一个长度为n(1e5)的数列。然后m(1e5)个询问。l,r,h
询问数列[l.r]中有多少个数字是不大于h的。
思路:
比较容易想到的是如果我们知道h是数列[l,r]里的第几大数。就能知道该区间有多少数不大于它了。区间第k大数明显是划分树的强项。但是我们不知道h是第几大数。所以就可以二分。然后问题就解决了。时间复杂度。O(n*log(n)^2)。还能接受。
详细见代码:
#include<algorithm>
#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=100010;
typedef long long ll;
int seg[20][maxn],lnum[20][maxn],sa[maxn];
int n,m;
void btree(int L,int R,int d)
{
    int i,ls,rs,lm,mid;
    if(L==R)
        return ;
    mid=(L+R)>>1;
    ls=L,rs=mid+1;
    lm=mid-L+1;
    for(i=L;i<=R;i++)
        if(seg[d][i]<sa[mid])
            lm--;
    for(i=L;i<=R;i++)
    {
        lnum[d][i]=(i==L)?0:lnum[d][i-1];
        if(seg[d][i]==sa[mid])
        {
            if(lm>0)
            {
                lm--;
                lnum[d][i]++;
                seg[d+1][ls++]=seg[d][i];
            }
            else
                seg[d+1][rs++]=seg[d][i];
        }
        else if(seg[d][i]<sa[mid])
        {
            lnum[d][i]++;
            seg[d+1][ls++]=seg[d][i];
        }
        else
            seg[d+1][rs++]=seg[d][i];
    }
    btree(L,mid,d+1);
    btree(mid+1,R,d+1);
}
int qu(int L,int R,int l,int r,int d,int k)
{
    int ss,s,bb,b,mid;
    if(L==R)
        return seg[d][L];
    ss=(l==L)?0:lnum[d][l-1];
    s=lnum[d][r]-ss;
    mid=(L+R)>>1;
    if(s>=k)
        return qu(L,mid,L+ss,L+ss+s-1,d+1,k);
    else
    {
        bb=l-L-ss;
        b=r-l+1-s;
        return qu(mid+1,R,mid+bb+1,mid+bb+b,d+1,k-s);
    }
}
void init()
{
    int i;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&seg[0][i]);
        sa[i]=seg[0][i];
    }
    sort(sa+1,sa+n+1);
    btree(1,n,0);
}
int bin(int L,int R,int h)
{
    int low=1,hi=R-L+1,mid,ans=0;
    while(low<=hi)
    {
        mid=(low+hi)>>1;
        if(qu(1,n,L,R,0,mid)<=h)
            ans=mid,low=mid+1;
        else
            hi=mid-1;
    }
    return ans;
}
int main()
{
    int L,R,h,t,cas=1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init();
        printf("Case %d:\n",cas++);
        while(m--)
        {
            scanf("%d%d%d",&L,&R,&h);
            L++,R++;
            printf("%d\n",bin(L,R,h));
        }
    }
    return 0;
}

还有一个更巧妙的方法就是离线树状数组。有时候离线真的能使原本复杂的问题简单很多。试想如果对于每个询问l,r,h我们只把值不大于h的数字插到树状数组对应位置。然后每次询问[l,r]中有多少个数被插进来了就行了。所以我们把数列按h排序。把询问也按h排序。然后对于每个询问先把h不大于询问的h的数列插进树状数组就行了。这样时间复杂度只有O(n*log(n))代码量也减了好多。
详细见代码:
#include<algorithm>
#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=100010;
typedef long long ll;
int C[maxn],ans[maxn],n,m;
struct qnode
{
    int l,r,h,id;
    inline bool operator <(const qnode &tt) const
    {
        return h<tt.h;
    }
} qu[maxn];
struct node
{
    int p,h;
    inline bool operator <(const node &tt) const
    {
        return h<tt.h;
    }
} bk[maxn];
void update(int x,int d)
{
    for(int i=x;i<=n;i+=i&-i)
        C[i]+=d;
}
int getsum(int x)
{
    int sum=0;
    for(int i=x;i>0;i-=i&-i)
        sum+=C[i];
    return sum;
}
int main()
{
    int t,cas=1,i,j;

    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        C[n]=0;
        for(i=0;i<n;i++)
        {
            C[i]=0,bk[i].p=i+1;
            scanf("%d",&bk[i].h);
        }
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&qu[i].l,&qu[i].r,&qu[i].h);
            qu[i].l++,qu[i].r++,qu[i].id=i;
        }
        sort(bk,bk+n);
        sort(qu,qu+m);
        for(i=j=0;i<m;i++)
        {
            for(;j<n&&bk[j].h<=qu[i].h;j++)
                update(bk[j].p,1);
            ans[qu[i].id]=getsum(qu[i].r)-getsum(qu[i].l-1);
        }
        printf("Case %d:\n",cas++);
        for(i=0;i<m;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值