CF1779D Boris and His Amazing Haircut

题目

CF1779D 题目传送门

分析

应该让每一个 \(x\) 覆盖尽量大的区间。

分析题目,可以知道对于每一个位置 \(i\),有多少 \(x > b[i]\) 覆盖了 \(i\) 是不关键的,关键是是否有一个 \(x \leq b[i]\) 是否覆盖了它。

当位置 \(i\)\(x < b[i]\) 覆盖,那这个位置会变成 \(x\) 而不是 \(b[i]\),就变不回来了,这是我们所不希望看到的。所以我们必须让 \(x \ge b[i]\) 覆盖 \(i\)。这样,就得出 \(x\) 不能覆盖 \(i\) 的条件:\(x < b[i]\)

有了上面的几个条件,我们就可以非常轻松的想出这题的解法:循环遍历每一个 \(i\),同时整一个 set 用来存目前的所有正在用的操作 \(x\)。循环过程中,有以下两种情况:

  1. 当前的 \(b[i]\) 包含在 set 中,那就什么也不干,继续往下走;
  2. 不包含,那就在所有还没用过的 \(x\) 中找一个与 \(b[i]\) 相等的 \(x\),并将这个 \(x\) 扔到 set 里,表示这个 \(x\) 正在被用,同时将其从还没用过的 \(x\) 中删掉。如果发现没有与 \(b[i]\) 相等的 \(x\) 没被用过,那就终止循环,答案为 NO。

在循环到某些位置 \(i\) 时,由于 set 中有些 \(x\) 会小于 \(b[i]\),所以需要实时地把这些 \(x\) 扔出去。因为一个操作的作用区间一定是连续的,所以如果这个 \(x\)\(b[i]\) 这个地方不能用了,那这后面一定都不能用这一个 \(x\)

附代码:

#include <iostream>              // 懒得用 bits 的屑博主
#include <set>
using namespace std;
int a[200005], b[200005];        // 如题目,顾名思义
multiset<int> st, oks;           // st 用来存还没有没用过的 x,oks 用来存正在被用的 x
int main() {
    set<int>::iterator it;       // set 的迭代器,用来遍历 set / multiset,应该是一种指针
    int tc, n, m;                // n, m 如题意
    scanf("%d", &tc);
    while (tc--) {
        oks.clear();             // 初始化
        scanf("%d", &n);
        for (int i = 0; i < n; i++) {
            scanf("%d", a + i);
        }
        for (int i = 0; i < n; i++) {
            scanf("%d", b + i);
        }
        scanf("%d", &m);
        st.clear();             // 初始化
        int tmp;
        for (int i = 0; i < m; i++) {
            scanf("%d", &tmp);
            st.insert(tmp);     // 这个 x 还没有被用过
        }
        bool flag = true;       // 答案
        for (int i = 0; i < n; i++) {
            if (b[i] > a[i]) {
                flag = false;   // 由于操作不能把一个数变大,所以就有了这句话
                break;          // 直接退循环
            } else if (b[i] == a[i]) {
                while (!oks.empty() && *oks.begin() < b[i]) { // 因为 set 默认从小到大排序,所以如果有一个 x 不合要求,
                    oks.erase(oks.begin());                   // 那这个 x 一定排在前面(x 的不合规性满足单调性)
                }
            } else {
                while (!oks.empty() && *oks.begin() < b[i]) oks.erase(oks.begin()); // 同上
                if (oks.count(b[i]) == 0 && st.count(b[i]) >= 1) { // 没有与 b[i] 相同的 x 正在被用且还有与 b[i] 相同的 x 可以用
                    st.erase(st.find(b[i]));	// 注意!这里的 st 是可重集,直接用 st.erase(b[i]) 会把所有 b[i] 全删了,导致错误
                    oks.insert(b[i]);	// x 正在被用
                } else if (oks.count(b[i]) == 0 && st.count(b[i]) == 0) { //没有与 b[i] 相同的 x 可用了,答案为 NO
                    flag = false;
                    break;
                } //此处省略了刚才讨论的第一种情况
            }
        }
        cout << (flag ? "YES\n" : "NO\n"); // yee~~~
    }
    return 0;
}

本蒟蒻的第一篇题解 完结撒花!!!

posted @ 2024-01-31 21:56  forgotmyhandle  阅读(22)  评论(0)    收藏  举报