主席树区间查询比某个值次小的数

本文深入探讨了单调栈和线段树两种数据结构的原理及应用,通过具体算法题目的解析,展示了如何利用这两种数据结构解决复杂的数据查询与更新问题。文章详细解释了线段树的构建、查询和更新操作,并结合单调栈进行优化,以提高算法效率。

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

题目链接:无聊的木头

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
static const int inf = 999999999;
static const int maxn = 1e5 + 5;
static const ll mod = 998244353;
struct Tree{
    int l, r;
    bool f;
}tr[maxn << 2];
int a[maxn], idx[maxn], mp[maxn * 10], ans[maxn];
void pushUp(int x){
    tr[x].f = tr[x << 1].f | tr[x << 1 | 1].f;
}
void build(int now, int l, int r){
    tr[now].l = l;
    tr[now].r = r;
    tr[now].f = 0;
    if(l == r) return ;
    int m = l + r >> 1;
    build(now << 1, l, m);
    build(now << 1 | 1, m + 1, r);
}
void update(int now, int x){
    if(tr[now].l == tr[now].r){
        tr[now].f = 1;
        return ;
    }
    if(tr[now << 1].r >= x) update(now << 1, x);
    else update(now << 1 | 1, x);
    pushUp(now);
}
int query(int now, int i, int j){
    if(i > j) return 0;
    if(!tr[now].f) return 0;    
    if(tr[now].l == tr[now].r) return tr[now].l;
    if(tr[now << 1].r >= j) return query(now << 1, i, j);
    if(tr[now << 1 | 1].l <= i) return query(now << 1 | 1, i, j);
    int res = query(now << 1 | 1, i, j);    //先跑右孩子
    if(res) return res;
    return query(now << 1, i, j);
}
int main(){
    int t;
    scanf("%d", &t);
    while(t--){
        memset(mp, 0, sizeof(mp));
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i){
            scanf("%d", &idx[i]);
            a[i] = idx[i];
        }
        sort(a + 1, a + n + 1);
        int cnt = unique(a + 1, a + n + 1) - a - 1;
        for(int i = 1; i <= n; ++i) idx[i] = lower_bound(a + 1, a + cnt + 1, idx[i]) - a;
        build(1, 1, cnt);
        for(int i = n; i >= 1; --i){
            int id = query(1, 1, idx[i] - 1);
            ans[i] = mp[id];
            update(1, idx[i]);
            mp[idx[i]] = i;     //多个符合条件选择下标最小的那个
        }
        for(int i = 1; i < n; ++i) printf("%d ", ans[i]);
        printf("0\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值