双周赛 57 题解

四道题都不难,中间两题卡了我比较长时间,幸亏最后一题是个单调栈一眼题,不然就要掉大分了

知识点:优先队列,差分数组,单调栈

检查是否所有字符出现次数相同

给定一个字符串 \(s\),如果 \(s\) 中所有字符串出现的次数一样,那么我们称之为好字符串,请返回 \(s\) 是否是好字符串

题解

把每一个字符出现的次数用桶维护,扔到集合里面去,利用集合的去重功能,判断集合的 size 是否为 \(1\)

class Solution {
public:
    bool areOccurrencesEqual(string s) {
        vector<int> a(26);
        int n = s.length();
        set<int> st;
        for (int i = 0; i < n; ++i) a[s[i] - 'a']++;
        for (int i = 0; i < 26; ++i)
            if (a[i]) st.insert(a[i]);
        return st.size() == 1;
    }
};

最小未被占据椅子的编号

\(n\) 个人在办聚会,有无限个椅子,每个人有到达时间和离开时间,每个人到达聚会的时候会选择空闲的最小编号的椅子

现在给定 \(n\) 个人的到达时间和离开时间,计算出第 \(t\) 个人坐的椅子的编号

题解

用一个优先队列存储每个椅子的空闲时间和编号,用一个集合存储 \(n\) 个椅子的编号

遍历 \(n\) 个人,根据到达时间和离开时间处理空闲和忙碌的椅子,更新优先队列和集合即可

// cpp
struct node {
    int idx, ed;
    node() {}
    node(int _idx, int _ed):
        idx(_idx), ed(_ed) {}
};
bool operator< (const node& x, const node& y) {
    if (x.ed == y.ed) return x.idx < y.idx;
    return x.ed > y.ed;
}

bool cmp(vector<int> &x, vector<int> &y) {
    if (x[0] == y[0]) return x[1] < y[1];
    return x[0] < y[0];
}
class Solution {
public:
    int smallestChair(vector<vector<int>>& t, int target) {
        vector<int> vec;
        int n = t.size();
        for (int i = 0; i < n; ++i) vec.push_back(i), t[i].push_back(i);
        set<int> st(vec.begin(), vec.end());
        priority_queue<node> pq;
        sort(t.begin(), t.end(), cmp);
        int res = 0;
        for (int i = 0; i < n; ++i) {
            while (!pq.empty()) {
                node temp = pq.top();
                if (temp.ed <= t[i][0]) pq.pop(), st.insert(temp.idx);
                else break;
            }
            res = *st.begin();
            pq.push(node(res, t[i][1]));
            st.erase(st.begin());
            if (t[i][2] == target) break;
        }
        return res;
    }
};

描述绘画结果

给定 \(n\) 个左闭右开区间 \([l,\ r)\),并给这个区间上每一个值加上 \(w\)

返回操作后的数轴,要求将相同值写成左闭右开区间的形式,值为 \(0\) 的区间跳过

举例来讲,如果 \(1,\ 2,\ 3\) 的值都是 \(12\),并且 \(4\) 的值为 \(13\),那我们可以把其合并成 \([1,\ 4),\ [4,\ 5)\)

注意,对于值相同的区间,如果达到这个和的方式不一样,不能写成一个区间

例如 \([1, 4),\ [4,\ 7)\) 均为 \(12\),但前者为 \(5 + 7\),后者为 \(3 + 9\),此时我们不能将其合并

题解

原题题意比较冗余,也很难抽象题意描述,但是考点很简单:差分数组

差分数组说的是:做 \(n\) 次区间加法操作,可以用离线算法在 \(O(n)\) 时间得到更新后的区间

具体来讲,如果要给区间 \([l,\ r)\) 统一加上 \(w\),只需要在 \(l\) 处加上 \(w\),在 \(r\) 处减去 \(w\),如此进行 \(n\) 次,最后对原数组进行前缀和,便可以得到更新后的数组,这里的「原数组」被称为差分数组

我们对更新后的数组进行划分,将相同的值归纳为一个区间即可

此题在差分数组的基础上多了一个要求:对于值相同的区间,如果达到这个和的方式不一样,不能写成一个区间

这个也比较好处理,利用贪心的思想,继续构造一个差分数组,区间加「区间右端点」的值即可,聪明的你能想明白贪心的正确性吗?

时间复杂度 \(O(n)\)

// cpp
typedef long long LL;
class Solution {
public:
    vector<vector<long long>> splitPainting(vector<vector<int>>& seg) {
        int n = seg.size();
        int maxx = 0;
        for (int i = 0; i < n; ++i) {
            maxx = max(maxx, seg[i][1]);
            seg[i].push_back(i + 1);
        }
        vector<LL> vec1(maxx + 7);
        vector<LL> vec2(maxx + 7);
        for (int i = 0; i < n; ++i) {
            int a = seg[i][0], b = seg[i][1], c = seg[i][2], d = seg[i][3];
            vec1[a] += c, vec1[b] -= c;
            vec2[a] += d, vec2[b] -= d;
        }
        for (int i = 1; i <= maxx; ++i) {
            vec1[i] += vec1[i - 1];
            vec2[i] += vec2[i - 1];
        }
        vector<vector<LL>> ans;
        int a = 1, b = 1;
        for (int i = 1; i <= maxx; ++i) {
            if (vec1[i] != vec1[a] || vec1[i] == vec1[a] && vec2[i] != vec2[a]) {
                if (!vec1[i - 1]) {a = b = i; continue;}
                else {
                    ans.push_back(vector<LL>{a, i, vec1[i - 1]});
                    a = b = i;
                }
            }
        }
        return ans;
    }
};

从队列中可以看到的人数

给定 \(n\) 个人及其身高 \(h_{i}\)

对于两个人 \(i,\ j\),其中 \(i\)\(j\) 左边,如果 \(i,\ j\) 中间的每一个人都没有他们高,那么 \(i\) 可以看到右边的 \(j\)

计算出每个人可以看到的右边的人数

数据规定

\(1\leq n\leq 10^5\)

题解

对于当前的 \(i\) 而言,我们可以计算出他左边第一个比他高的人 \(L\),以及右边第一个比他高的人 \(R\)

如果 \(L\) 存在,那么 \(L\) 可以看到 \(i\)

如果 \(R\) 存在,那么 \(i\) 可以看到 \(R\)

使用单调栈预处理即可,时间复杂度 \(O(n)\)

// cpp
class Solution {
public:
    vector<int> canSeePersonsCount(vector<int>& h) {
        int n = h.size();
        vector<int> rmax(n + 7);
        vector<int> lmax(n + 7);
        vector<int> ans(n + 7);
        vector<int> a(n + 7);
        for (int i = 1; i <= n; ++i) a[i] = h[i - 1];
        stack<int> mono1, mono2; // 递增
        for (int i = 1; i <= n; ++i) {
            while (!mono1.empty() && a[mono1.top()] < a[i]) {
                rmax[mono1.top()] = i;
                mono1.pop();
            }
            mono1.push(i);
        }
        for (int i = n; i >= 1; --i) {
            while (!mono2.empty() && a[mono2.top()] < a[i]) {
                lmax[mono2.top()] = i;
                mono2.pop();
            }
            mono2.push(i);
        }
        for (int i = 1; i <= n; ++i) {
            int L = lmax[i], R = rmax[i];
            if (L >= 1 && L <= n) ans[L]++;
            if (R >= 1 && R <= n) ans[i]++;
        }
        return {ans.begin() + 1, ans.begin() + 1 + n};
    }
};
posted @ 2021-07-25 22:14  徐摆渡  阅读(44)  评论(0编辑  收藏  举报