ZJYYCOJ-商店繁荣度

题目链接: http://acm.oinsm.com/problem.php?cid=1027&pid=4

在这里插入图片描述

思路

今天周赛的一道题目,刚看到以为是一道单调栈的题目,后面写完,交了好几发,搓了一个样例,结果发现就错了!!这个8被丢掉了!所以还剩不到10分钟的时候立马转变了思路,用vector来存,从右往左,每次比较最后一个数如果比它小则加入vector,否则,就二分去找答案!

5
10 3 6 2 8

所以思路1: 单调序列 + 二分
思路2: 线段树:
题目: 对于每个数查找他右边的比他小的数且离他远的数,所有我们可以通过线段树来维护区间的最小值,对于这个数列,我们从右往左查询! 用INF初始化整个线段树(1-n),从右往左查询,每查询完一个数,就用单点修改把这个数加入到线段树中,然后继续往左.
对于每次查询,我们先右后左(因为我们是从右往左放入数),如果左右节点的值小于你要查询的值,则继续递归,直到到达叶节点返回即可!

代码

  • 单调序列 + 二分
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
const long long INF = 0x3f3f3f3f;
typedef long long ll;
int num[MAXN];
int ans[MAXN];
int main()
{
    ios::sync_with_stdio(false);
    int n;
    while(cin >> n) {
        for(int i = 1; i <= n; i++) cin >> num[i];
        vector<pair<int, int> > ve;
        ve.clear();
        for(int i = n; i >= 1; i--) {
            if(ve.size() == 0 || num[i] <= ve.back().second) {
                ve.emplace_back(i, num[i]);
                ans[i] = -1;
            }
            else {
//                cout << "**************" << endl;
//                for(auto j : ve) {
//                    cout << j.second << " ";
//                }
//                cout << "**************" << endl;
                int l = 0, r = ve.size() - 1;
                ans[i] = -1;
                while(l <= r) {
                    int mid = (l + r) / 2;
//                    cout << mid << endl;
                    if(ve[mid].second > num[i]) {
                        l = mid + 1;
                    }
                    else {
                        r = mid - 1;
                        ans[i] = max(ans[i], ve[mid].first - i - 1);
                    }
                }

            }
        }
        for(int i = 1; i <= n; i++) {
            if(i != 1) cout << " ";
            cout << ans[i];
        }
        cout << endl;
    }
    return 0;
}
  • 线段树
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
const int INF = 1e9 + 7;
pair<int, int> node[MAXN<<2];
int num[MAXN];
//线段树模板(以区间和为例)
//更新数据
inline void PushUp(int root)
{
    node[root] = (node[root<<1].first > node[root<<1|1].first) ? node[root<<1|1] : node[root<<1];
}
//1.建树
inline void BuildTree(int root, int l, int r)     //[l,r]当前节点的区间, root 当前节点的编号
{
    node[root].first = INF;    //建树先左后右
    node[root].second = -1;
    if(l == r){
        //说明当前已经到达叶子节点
        node[root].first = INF;    //建树先左后右
        node[root].second = -1;
        return ;
    }
    int mid = (l + r) >> 1;
    //继续递归建树
    BuildTree(root<<1, l, mid);//左
    BuildTree(root<<1|1, mid + 1, r);//右

    //其他操作(例如更新区间和)
    PushUp(root);

}
//2.单点修改
inline void Update(int root, int l, int r, int index, pair<int, int> value)
{
    // root 当前节点编号 [l,r] 树的区间范围, index 修改的节点编号, value 修改的值
    if(l == r){
        //到达当前节点
        node[root] = value;
        return ;
    }
    int mid = (l + r) >> 1;
    if(index <= mid)
        Update(root<<1, l, mid, index, value);
    else
        Update(root<<1|1, mid + 1, r, index, value);

    //修改后记得更新
    PushUp(root);
}
//4.区间查询
inline pair<int, int> Query(int root, int l, int r, int value)
{
    // root 当前节点的编号, [l,r] 线段树区间, [L,R] 需要修改的区间范围
    if(l == r){
        //当前区间在查询区间内, 直接返回节点值
//        cout << "------------" << endl;
//        cout << root << " " << node[root].second << endl;
//        cout << "------------" << endl;
        return node[root];
    }
    int mid = (l + r) >> 1;

    pair<int, int> a, b;
    a.first = a.second = b.first = b.second = -1;
    if(node[root<<1|1].first < value)
        return Query(root<<1|1, mid + 1, r, value);
    if(node[root<<1].first < value)
        return Query(root<<1, l, mid, value);

    return {-1, -1};
}
int ans[MAXN];
int main()
{
    int n;
    while(scanf("%d", &n) != EOF) {
        for(int i = 1; i <= n; i++) {
            scanf("%d", &num[i]);
        }
        BuildTree(1, 1, n);
        for(int i = n; i >= 1; i--) {
            pair<int, int> tmp = Query(1, 1, n, num[i]);
//            cout << i << " " << tmp.first << " " << tmp.second << endl;
            if(tmp.first == -1) {
                ans[i] = -1;
            }
            else {
                ans[i] = tmp.second - i - 1;
            }
            Update(1, 1, n, i, {num[i], i});
        }

        for(int i = 1; i <= n; i++) {
            if(i != 1) printf(" ");
            printf("%d", ans[i]);
        }
        printf("\n");
    }

    return 0;
}

代码写的可能不好,思路到位了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值