原题链接:Problem - D - Codeforces
题意:
给定长度为 n 的数组 A ,代表 Boris 现在的头发长度,和一个长度为 n 的数组 B ,代表他希望的发型的头发长度。理发师手里有 m 把剪刀,每个都只能用一次,剪刀的所剪的高度用 xi 给出。
对于每一把未使用过的推子:
理发师可以选择一个 [l,r] 区间;
将该区间的所有头发 ai 修建为 min(ai,x) 。
请问理发师用手中的这些推子,能不能剪完 Boris 的发型。
思路:显然,若存在a[i]
首先,我们可以选择两个相同的数作为端点,通过样例一可以看出,如果端点内的数都小于等于端点,那么这个修剪区间就是合法的,因为我们可以先将这个区间,都变为小于等于端点的长度,再让区间内小于端点的数通过其它操作变小。反之,若一个区间内有大于端点的数,那么这个修剪区间就是不合法的,因为若你修剪了这个区间,就会使这个大于端点的值变小,它就变不回来了,这样就算a数组再怎么操作也变不成b数组了。
题目中有一个很贴心的样例,我们来看看它:
这两个样例的唯一的区别是什么呢?就是给的剪刀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;
}