原题链接:Problem - D - Codeforces
题意:
给定长度为 n 的数组 A ,代表 Boris 现在的头发长度,和一个长度为 n 的数组 B ,代表他希望的发型的头发长度。理发师手里有 m 把剪刀,每个都只能用一次,剪刀的所剪的高度用 xi 给出。
对于每一把未使用过的推子:
理发师可以选择一个 [l,r] 区间;
将该区间的所有头发 ai 修建为 min(ai,x) 。
请问理发师用手中的这些推子,能不能剪完 Boris 的发型。
思路:显然,若存在a[i]<b[i],则输出NO,因为剪头发只能让a[i]变短,不能让a[i]变长。然后,若a[i]!=b[i]我们才需要操作它,否则不需要。其实,判断完这两步数组a基本没什么用了,以下都是基于对数组b的操作的解释,主要说明的是如何让相同类型剪刀的使用次数变少,并且操作合法。
首先,我们可以选择两个相同的数作为端点,通过样例一可以看出,如果端点内的数都小于等于端点,那么这个修剪区间就是合法的,因为我们可以先将这个区间,都变为小于等于端点的长度,再让区间内小于端点的数通过其它操作变小。反之,若一个区间内有大于端点的数,那么这个修剪区间就是不合法的,因为若你修剪了这个区间,就会使这个大于端点的值变小,它就变不回来了,这样就算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<int, int> mp;//我们能用的各个剪刀数
map<int, vector<int>> mp2;//需要操作的各个数的下标
map<int, int> 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]<b[i],则输出NO,因为剪头发只能让a[i]变短,不能让a[i]变长
no;
return;
}
if (a[i] != b[i]) mp3[b[i]]++;
}
for (auto [x, v] : mp2) {
int p1 = 0, p2 = 1;
while (p1 < v.size() && p2 < v.size()) {
if (query(v[p1] + 1, v[p2] - 1) <= x) mp3[x]--, p2++;//若区间合法,就合并下标,并且使需要的剪刀数减1
else p1 = p2,p2++;//若区间不合法,就找下一个区间
}
}
for (auto [x, y] : mp3) {
if (mp3[x] > mp[x]) {//若需要的剪刀数超过了拥有的剪刀数,则输出NO
no;
return;
}
}
yes;
}