第 349 场力扣周赛

A 既不是最小值也不是最大值

在这里插入图片描述

排序后取中间

class Solution {
  public:
    int findNonMinOrMax(vector<int>& nums) {
        if (nums.size() < 3)
            return -1;
        sort(nums.begin(), nums.end());
        return nums[1];
    }
};

B 执行子串操作后的字典序最小字符串

在这里插入图片描述
模拟: 找到第一个不是’a’的位置, 以其为操作区间的左端点, 操作区间的右端点为从左端点开始连续的最后一个不为’z’的位置, 若串中字符全为’a’,则只操作最后一个位置.

class Solution {
  public:
    string smallestString(string s) {
        int n = s.size();
        int i = 0;
        for (; i < n; i++)
            if (s[i] != 'a')
                break;
        if (i == n) {
            s[n - 1] = 'z';
            return s;
        }
        for (; i < n && s[i] != 'a'; i++)
            s[i] -= 1;
        return s;
    }
};

C 收集巧克力

在这里插入图片描述

枚举: 显然最优方案的操作次数最多为 n − 1 n-1 n1, 所以可以枚举方案的操作次数, 当操作次数为 o p op op时, 最初坐标 i i i可以选择 [ i , i + o p − 1 ] [i,i+op-1] [i,i+op1] (若 i + o p − 1 ≥ n − 1 i+op-1 \ge n-1 i+op1n1则为 [ i , n − 1 ] ∪ [ 0 , o p + i − n ] [i,n-1] \cup [0,op+i-n] [i,n1][0,op+in])区间内的任意值, 可以提前预处理求出所有区间的最小值

class Solution {
  public:
    typedef long long ll;
    long long minCost(vector<int>& a, int x) {
        int n = a.size();
        int mn[n][n];//区间最小值
        for (int i = 0; i < n; i++)
            mn[i][i] = a[i];
        for (int len = 2; len <= n; len++)
            for (int i = 0; i + len - 1 < n; i++)
                mn[i][i + len - 1] = min(mn[i][i + len - 2], a[i + len - 1]);
        ll res = INT64_MAX;
        for (int op = 0; op < n; op++) {
            ll cur = 1LL * op * x;
            for (int i = 0; i < n; i++) {
                ll t = i + op < n ? mn[i][i + op] : min(mn[i][n - 1], mn[0][op + i - n]);
                cur += t;
            }
            res = min(res, cur);
        }
        return res;
    }
};

D 最大和查询

在这里插入图片描述
在这里插入图片描述

区间最值: 1) 设两个数组为 a a a b b b, 若存在 i i i, j j j使得 a i < a j a_i<a_j ai<aj b i < b j b_i<b_j bi<bj, 则 ( a i , b i ) (a_i, b_i) (ai,bi)的和不会是任意查询的答案, 所以可以从数组中删除 ( a i , b i ) (a_i, b_i) (ai,bi). 要删掉所以这样的数可以先将数对数组 { ( a k , b k ) } \{(a_k,b_k)\} {(ak,bk)}非降序排序, 再用单调栈的方法将数对数组的 b k b_k bk序列调整成非升序. 2)之后针对每一个查询 < x , y > <x,y> <x,y>, 用二分的方法, 只考虑 x x x将得到数对数组上满足条件的下标区间 [ r i g h t , n − 1 ] [right,n-1] [right,n1], 类似只考虑 y y y将得到数对数组上满足条件的下标区间 [ 0 , l e f t ] [0,left] [0,left], 同时考虑 x x x y y y满足的区间即为 [ l e f t , r i g h t ] [left,right] [left,right](若两区间交集为空返回-1), 通过线段树求区间最大值.

typedef long long ll;
const int maxn = 1e5 + 5;
struct SegmentTree {
    ll tl;
    ll tr;
    ll s;
    ll mark;
    int lazy;
};
SegmentTree st[maxn * 4];

inline void push_down(ll index) {
    st[index << 1].lazy = 1;
    st[index << 1 | 1].lazy = 1;
    st[index << 1].mark += st[index].mark;
    st[index << 1 | 1].mark += st[index].mark;
    st[index << 1].s += st[index].mark;
    st[index << 1 | 1].s += st[index].mark;

    st[index].lazy = 0;
    st[index].mark = 0;
}

inline void push_up(ll index) {
    st[index].s = max(st[index << 1].s, st[index << 1 | 1].s);
}

void build(ll l, ll r, vector<pair<int, int>> &li0, ll index = 1) {
    st[index].tl = l;
    st[index].tr = r;
    st[index].lazy = 0;
    st[index].mark = 0;
    if (l == r) {
        st[index].s = li0[l - 1].first + li0[l - 1].second;
    } else {
        ll mid = (l + r) >> 1;
        build(l, mid, li0, index << 1);
        build(mid + 1, r, li0, index << 1 | 1);
        push_up(index);
    }
}


ll query(ll l, ll r, ll index = 1) {
    if (l <= st[index].tl and st[index].tr <= r) {
        return st[index].s;
    } else {
        if (st[index].lazy)
            push_down(index);
        if (r <= st[index << 1].tr)
            return query(l, r, index << 1);
        else if (l > st[index << 1].tr)
            return query(l, r, index << 1 | 1);
        return max(query(l, r, index << 1), query(l, r, index << 1 | 1));
    }
}

class Solution {
public:
    vector<int> maximumSumQueries(vector<int> &a1, vector<int> &a2, vector<vector<int>> &queries) {
        int n = a1.size();
        vector <pair<int, int>> li, st;
        for (int i = 0; i < n; i++)
            li.emplace_back(a1[i], a2[i]);
        sort(li.begin(), li.end());
        for (auto x: li) {//st模拟栈, 最终{st[i].first}非降序, {st[i].second}非升序
            while (!st.empty() && st.back().second < x.second)
                st.pop_back();
            st.push_back(x);
        }
        n = st.size();//更新n
        build(1, n, st);//建树
        vector<int> res;
        for (auto &qi: queries) {
            int left, right;
            int l = 0, r = n, mid;
            while (l < r) {
                mid = (l + r) / 2;
                if (st[mid].first >= qi[0])
                    r = mid;
                else
                    l = mid + 1;
            }
            left = l;//满足qi[0]的区间为[left,n-1]
            l = -1, r = n - 1;
            while (l < r) {
                mid = (l + r + 1) / 2;
                if (st[mid].second >= qi[1])
                    l = mid;
                else
                    r = mid - 1;
            }
            right = l;//满足qi[1]的区间为[left,n-1]
            if (left <= right)
                res.push_back(query(left + 1, right + 1));
            else
                res.push_back(-1);
        }
        return res;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值