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

区间查询<=h的数有几个,用主席树可以直接做,只要修改一下query,二分统计个数即可。
注意砖块高度和跳跃高度都要离散化,空间要开2*10^5。
不过网上的普遍做法是线段树离线处理,确实比主席树巧妙得多,把询问和数据按高度从小到大排序,每次只要把<=h的数据插入线段树,直接查询就行,因为后面插入的>h的高度对前面的答案没有影响,根本没必要记录状态。而查询第k大要用主席树记录所有状态,是因为读取了所有数据才能比较大小。
离线处理还是考验思维的灵活性,多留意是否所有的数据对每次查询都有用。
这次只写了主席树,有空再补一下线段树的写法。

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 200010;
const int M = MAXN * 30;
int x,n,q,m,tot;
int a[MAXN], t[MAXN],b[MAXN],L[MAXN],R[MAXN];
int T[M], lson[M], rson[M], c[M];

void Init_hash()
{
    for(int i = 1; i <= n;i++)
        t[i] = a[i];
    for(int i=1;i<=q;i++)
        t[i+n]=b[i];
    sort(t+1,t+1+n+q);
    m = unique(t+1,t+1+n+q)-t-1;
}
int build(int l,int r)
{
    int root = tot++;
    c[root] = 0;
    if(l != r)
    {
        int mid = (l+r)>>1;
        lson[root] = build(l,mid);
        rson[root] = build(mid+1,r);
    }
    return root;
}
int has(int x)
{
    return lower_bound(t+1,t+1+m,x) - t;
}
int update(int root,int pos,int val)
{
    int newroot = tot++, tmp = newroot;
    c[newroot] = c[root] + val;
    int l = 1, r = m;
    while(l < r)
    {
        int mid = (l+r)>>1;
        if(pos <= mid)
        {
            lson[newroot] = tot++; rson[newroot] = rson[root];
            newroot = lson[newroot]; root = lson[root];
            r = mid;
        }
        else
        {
            rson[newroot] = tot++; lson[newroot] = lson[root];
            newroot = rson[newroot]; root = rson[root];
            l = mid+1;
        }
        c[newroot] = c[root] + val;
    }
    return tmp;
}
int query(int left_root,int right_root,int k)
{
    int l = 1, r = m, ans = 0;
    while( l < r)
    {
        int mid = (l+r)>>1;
        if(mid <= k )
        {
            ans+=c[lson[left_root]]-c[lson[right_root]];
            l = mid+1;
            left_root = rson[left_root];
            right_root = rson[right_root];
        }
        else
        {
            r=mid;
            left_root = lson[left_root];
            right_root = lson[right_root];
        }
    }
    return ans;
}
int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%d",&x);
    int l,r,k,y=0;
    while(x--)
    {
        y++;
        scanf("%d%d",&n,&q);
        tot = 0;
        for(int i = 1;i <= n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=q;i++){
            scanf("%d%d%d",&l,&r,&k);
            b[i]=k;
            L[i]=l+1;
            R[i]=r+1;
        }
        Init_hash();
        T[n+1] = build(1,m);
        for(int i = n;i ;i--)
        {
            int pos = has(a[i]);
            T[i] = update(T[i+1],pos,1);
        }
        printf("Case %d:\n",y);
        for(int i=1;i<=q;i++)
        {
            l=L[i];
            r=R[i];
            k=has(b[i]);
            printf("%d\n",query(T[l],T[r+1],k));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值