CodeForces - 91B Queue 单调队列 | 线段树

本文探讨了两种高效算法,用于解决在给定序列中寻找每个元素右侧最近且值小于当前元素的问题。通过使用单调队列和线段树,文章详细介绍了算法原理、实现步骤,并提供了C++代码示例。

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

题目

给出n个数的序列,求出每一个数右边最后一个比他小的数的位置差。


题解

考虑单调队列做法

维护一个单调递减的队列。

对于一个数来说 最优被定义为 尽可能小,同时位置要小。

于是对于一个新入队的数来说,如果他比最小的数大的话。其位置也大于最小的数。则这个数是无用的。

于是对于每个新入队的数,在入队前在单调队列中二分查找一下最后一个比他的小的数的位置。

如果满足单调性则入队。否则直接不考虑。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int n,arr[maxn],ans[maxn];
typedef pair<int,int> pint;
pint qu[maxn],tmp;
int main()
{
    while(~scanf("%d",&n)) {
        for(int i=n;i>=1;i--) scanf("%d",&arr[i]);
        for(int i=1,h=1,t=0;i<=n;i++) {
            tmp = make_pair(i,arr[i]);
            if(h > t || (h <= t && qu[t].second >= arr[i])) {
                ans[i] = -1;
                qu[++t] = tmp;
            }
            else {
                int l = h,r = t;
                while(l <= r) {
                    int mid = (l+r)>>1;
                    if(qu[mid].second < arr[i]) r = mid - 1;
                    else l = mid + 1;
                }
                ans[i] = i - qu[l].first - 1;
            }
        }
        for(int i=n;i>=2;i--) printf("%d ",ans[i]);
        printf("%d\n",ans[1]);
    }
    return 0;
}

线段数做法

线段树维护区间最小值。

初始化无穷INF

每次递归查询离当前位置最远的小于该数的位置。

之后更新该点的值。

#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5+10;
struct SegTree {
    int Min[maxn<<2];
    void push_up(int rt) {
        Min[rt] = min(Min[rt<<1],Min[rt<<1|1]);
    }
    void build(int l,int r,int rt) {
        if(l == r) {
            Min[rt] = INF;
            return;
        }
        int mid = (l+r)>>1;
        build(l,mid,rt<<1);
        build(mid+1,r,rt<<1|1);
        push_up(rt);
    }
    void update(int pos,int val,int l,int r,int rt) {
        if(l == r) {
            Min[rt] = val;
            return ;
        }
        int mid = (l+r)>>1;
        if(pos <= mid) update(pos,val,l,mid,rt<<1);
        else update(pos,val,mid+1,r,rt<<1|1);
        push_up(rt);
    }
    int query(int val,int l,int r,int rt) {
        if(l == r) return l;
        int mid = (l+r)>>1;
        if(Min[rt<<1] < val) return query(val,l,mid,rt<<1);
        if(Min[rt<<1|1] < val) return query(val,mid+1,r,rt<<1|1);
    }
}seg;
int n,arr[maxn],ans[maxn];
int main()
{
    while(~scanf("%d",&n)) {
        seg.build(1,n,1);
        for(int i=n;i>=1;i--) scanf("%d",&arr[i]);
        for(int i=1;i<=n;i++) {
            if(seg.Min[1] >= arr[i]) {
                ans[i] = -1;
            }
            else {
                ans[i] = i - seg.query(arr[i],1,n,1) - 1;
            }
            seg.update(i,arr[i],1,n,1);
        }
        for(int i=n;i>1;i--) printf("%d ",ans[i]);
        printf("%d\n",ans[1]);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值