Hello 2023 D. Boris and His Amazing Haircut

原题链接:Problem - D - Codeforces

题意:

给定长度为 n 的数组 A ,代表 Boris 现在的头发长度,和一个长度为 n 的数组 B ,代表他希望的发型的头发长度。理发师手里有 m 把剪刀,每个都只能用一次,剪刀的所剪的高度用 xi 给出。
对于每一把未使用过的推子:
理发师可以选择一个 [l,r] 区间;
将该区间的所有头发 ai 修建为 min(ai,x) 。
请问理发师用手中的这些推子,能不能剪完 Boris 的发型。

思路:显然,若存在a[i]

首先,我们可以选择两个相同的数作为端点,通过样例一可以看出,如果端点内的数都小于等于端点,那么这个修剪区间就是合法的,因为我们可以先将这个区间,都变为小于等于端点的长度,再让区间内小于端点的数通过其它操作变小。反之,若一个区间内有大于端点的数,那么这个修剪区间就是不合法的,因为若你修剪了这个区间,就会使这个大于端点的值变小,它就变不回来了,这样就算a数组再怎么操作也变不成b数组了。

题目中有一个很贴心的样例,我们来看看它:

Hello 2023 D. Boris and His Amazing Haircut_第1张图片

这两个样例的唯一的区别是什么呢?就是给的剪刀5的数量。我们来看看为什么使用剪刀5数量至少是2个。

首先我们判断下需要修剪的5的地方,就是这: 

还有两个5没做上标记,是因为a[i]也是5,我们不用管它。

所以一开始我们需要3个剪刀5。

我们来逐一检查区间是否合法:

这个区间内所有的数都小于等于它的端点,也就是5,所以这个区间合法,我们可以只用一次操作就能将两个点变成5,这样需要剪刀5的数量-1,变成2了。

接下来我们来看下一个区间:

区间内就一个数,但是这个数比5大,也就是说,我们不能将第三个5合并到第一个区间内,对于数字5,我们剩下了两个区间。最后对于数字5,我们需要修剪的就是两个区间,也就是消耗了两把剪刀。

总结一下,这道题主要考察快速求区间最值,用树状数组、线段树、栈都能写,我用的是树状数组,然后存一下下标判断剪刀是否足够就行,具体实现见代码注释

int lowbit(int x) {
    return x & (-x);
}
void update(int x) {
    int lx, i;
    while (x <= n) {
        h[x] = b[x];
        lx = lowbit(x);
        for (i = 1; i < lx; i <<= 1) h[x] = max(h[x], h[x - i]);
        x += lowbit(x);
    }
}
int query(int x, int y) {
    int ans = 0;
    while (y >= x) {
        ans = max(b[y], ans);
        y--;
        for (; y - lowbit(y) >= x; y -= lowbit(y)) ans = max(h[y], ans);
    }
    return ans;
}
void solve() {
    map mp;//我们能用的各个剪刀数 
    map> mp2;//需要操作的各个数的下标 
    map mp3;//我们需要的剪刀数 
    cin >> n;
    FOR(1, n) h[i] = 0;
    FOR(1, n) cin >> a[i];
    FOR(1, n) {
        cin >> b[i];
        if (a[i] != b[i]) mp2[b[i]].push_back(i);//若a[i]!=b[i]则说明需要操作。 
        update(i);
    }
    cin >> m;
    FOR(1, m) {
        cin >> x;
        mp[x]++; //存能用的各个剪刀数 
    }
    FOR(1, n) {
        if (a[i] < b[i]) {//若存在a[i] mp[x]) {//若需要的剪刀数超过了拥有的剪刀数,则输出NO 
            no;
            return;
        }
    }
    yes;
}

你可能感兴趣的:(codeforces题解,算法,c++,数据结构)