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

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

2012 ACM/ICPC Asia Regional Hangzhou Online

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;
}

时间: 2024-10-27 02:53:00

hdu 4417 Super Mario(离线树状数组|划分树)的相关文章

HDU 4417 Super Mario(离线线段树or树状数组)

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 l

hdu-4417 Super Mario(树状数组 + 划分树)

题目链接: Super Mario Time Limit: 2000/1000 MS (Java/Others)     Memory Limit: 32768/32768 K (Java/Others) Problem Description Mario is world-famous plumber. His “burly” figure and amazing jumping ability reminded in our memory. Now the poor princess is

HDU 4417 Super Mario ( 超级马里奥 + 主席树 + 线段树/树状数组离线处理 + 划分树 )

HDU 4417 - Super Mario ( 主席树 + 线段树/树状数组离线处理 + 划分树 ) 这道题有很多种做法,我先学习的是主席树.后面陆续补上线段树离线和划分树 题目大意就是给定一个区间给定一个数列,每次要求你查询区间[L,R]内不超过K的数的数量 主席树做法: 最基本的是静态第k大,这里是求静态的 <= K,差不多,在Query操作里面需要修改修改 先建立size棵主席树,然后询问的时候统计的是 第R棵主席树中[1,K]的数量 - 第L-1棵主席树中[1,K]的数量 注意这里下标

HDU 1394 Minimum Inversion Number 树状数组&amp;&amp;线段树

题目给了你一串序列,然后每次 把最后一个数提到最前面来,直到原来的第一个数到了最后一个,每次操作都会产生一个新的序列,这个序列具有一个逆序数的值,问最小的你逆序数的值为多少 逆序数么 最好想到的是树状数组,敲了一把很快,注意把握把最后一个数提上来对逆序数的影响即可, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #i

hdu 1166 树状数组 线段树

敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 51177    Accepted Submission(s): 21427 Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务

HDU 1166 敌兵布阵 (树状数组&#183;线段树)

题意  中文 动态区间和问题   只会更新点  最基础的树状数组 线段树的应用 树状数组代码 #include <bits/stdc++.h> using namespace std; const int N = 50005; int c[N], n, m; void add(int p, int x) { while(p <= n) c[p] += x, p += p & -p; } int getSum(int p) { int ret = 0; while(p > 0

【bzoj3744】Gty的妹子序列 分块+树状数组+主席树

题目描述 我早已习惯你不在身边, 人间四月天 寂寞断了弦. 回望身后蓝天, 跟再见说再见…… 某天,蒟蒻Autumn发现了从 Gty的妹子树(bzoj3720) 上掉落下来了许多妹子,他发现 她们排成了一个序列,每个妹子有一个美丽度. Bakser神犇与他打算研究一下这个妹子序列,于是Bakser神犇问道:"你知道区间 [l,r]中妹子们美丽度的逆序对数吗?" 蒟蒻Autumn只会离线乱搞啊……但是Bakser神犇说道:"强制在线." 请你帮助一下Autumn吧.

hdu1394(枚举/树状数组/线段树单点更新&amp;区间求和)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 题意:给出一个循环数组,求其逆序对最少为多少: 思路:对于逆序对: 交换两个相邻数,逆序数 +1 或 -1, 交换两个不相邻数 a, b, 逆序数 += 两者间大于 a 的个数 - 两者间小于 a 的个数: 所以只要求出初始时的逆序对数,就可以推出其余情况时的逆序对数.对于求初始逆序对数,这里 n 只有 5e3,可以直接暴力 / 树状数组 / 线段树 / 归并排序: 代码: 1.直接暴力 1

【bzoj1146】[CTSC2008]网络管理Network 倍增LCA+dfs序+树状数组+主席树

题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由器老化,在这些