【主席树】HDU 4417 Super Mario(查询区间中小于val的值的个数【前缀和相减】)

HDU 4417 Super Mario

题意:查询区间中小于val的值的个数

思路:除了查询操作需要改成前缀和相减之外,其他的都是主席树查找第k小值的模板。【OS:就这个所谓的之外让我想了两个多小时,最后还是看的别个博客www,tcl,就各种互相乱想都没有想到相减就可的思路,TAT】

注意:题目要求区间为[0, n - 1],但是我还是建的[1, n](比较舒服),就是查询的时候区间需要注意一下就好了。另外,一定要判断查询区间是不是存在,不存在输出0,否则query就死循环了啊TAT,T了一发。www

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define lowbit(x) x & (-x)

#define MID (l + r ) >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid + 1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define eps  1e-6

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 1e5 + 5;
int n, m, tot, UP;
struct node{
    int ls, rs, num;
    node(int a = 0, int b = 0, int c = 0): ls(a), rs(b), num(c) {}
}tree[maxN * 20];
int root[maxN];
vector<int>vt, disc;
void init()
{
    vt.clear();
    disc.clear();
    tot = 0;
}
void update(int &now, int pre, int l, int r, int pos)
{
    now = ++ tot;
    tree[now] = node(tree[pre].ls, tree[pre].rs, tree[pre].num + 1);
    if(l == r) return ;
    int mid = MID;
    if(mid >= pos) update(tree[now].ls, tree[pre].ls, l, mid, pos);
    else update(tree[now].rs, tree[pre].rs, mid + 1, r, pos);
}
int query(int i, int j, int l, int r, int ql, int qr)//区间[i + 1, j]中有多少值在[ql, qr]之间的数
{
    if(ql <= l && qr >= r)
        return tree[j].num - tree[i].num;
    int mid = MID;
    if(qr <= mid) return query(tree[i].ls, tree[j].ls, l, mid, ql, qr);
    else if(ql > mid) return query(tree[i].rs, tree[j].rs, mid + 1, r, ql, qr);
    else return query(tree[i].ls, tree[j].ls, l, mid, ql, qr) + query(tree[i].rs, tree[j].rs, mid + 1, r, ql, qr);
}
int main()
{
    int Case = 0;
    int TAT; scanf("%d", &TAT);
    while(TAT -- )
    {
        init();
        scanf("%d%d", &n, &m);
        for(int i = 0; i < n; i ++ )
        {
            int a; scanf("%d", &a);
            vt.push_back(a); disc.push_back(a);
        }
        sort(disc.begin(), disc.end());
        disc.erase(unique(disc.begin(), disc.end()), disc.end());
        UP = disc.size();
        for(int i = 1; i <= n; i ++ )
        {
            int th = lower_bound(disc.begin(), disc.end(), vt[i - 1]) - disc.begin() + 1;
            update(root[i], root[i - 1], 1, UP, th);
        }
        printf("Case %d:\n", ++ Case);
        while( m -- )
        {
            int l, r, up; scanf("%d%d%d", &l, &r, &up);
            int id = upper_bound(disc.begin(), disc.end(), up) -disc.begin() + 1;//第一个大于up的数对应的离散化值
            if(id - 1 == 0) { printf("0\n"); continue; }//没有大于up的数。不然查询区间左端点大于右端点,死循环了www
            printf("%d\n", query(root[l], root[r + 1], 1, UP, 1, id - 1));//id - 1就是第一个小于等于up的数对应的离散化值
        }
    }
   return 0;
}
/*
1
5 10
1 2 3 4 5
0 1 0
 */
发布了180 篇原创文章 · 获赞 54 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44049850/article/details/103986679