P4392 [BOI2007]Sound 静音问题
题目传送门
分析
本题其实可以拆解程两个问题,求区间最大值、求区间最小值。
而这两个操作都必须在 \(O(\log n)\) 内完成。
自然想到线段树。
细节问题
线段树需要维护两个信息:区间最大值、区间最小值。
输入完数据后,建树。然后遍历序列,求区间最大值最小值之差,再判断是否大于 \(c\) 就可以了。
记得立一个 \(flag\) 方便记录是否有解。(很重要)。
挂代码
考场代码如下:
#include <bits/stdc++.h>
using namespace std;
int n, m, c, a[1000005];
int maxn[1000005 << 2], minn[1000005 << 2];
bool flag = true;
void pushup(int i) {
maxn[i] = max(maxn[i << 1], maxn[i << 1 | 1]);
minn[i] = min(minn[i << 1], minn[i << 1 | 1]);
}
void build(int i, int l, int r) {
if (l == r) {
maxn[i] = minn[i] = a[l];
return;
}
int mid = (l + r) >> 1;
build(i << 1, l, mid);
build(i << 1 | 1, mid + 1, r);
pushup(i);
}
int querymax(int L, int R, int l, int r, int i) {
if (L <= l && r <= R) {
return maxn[i];
}
int mid = (l + r) >> 1;
int result = INT_MIN;
if (L <= mid) {
result = max(result, querymax(L, R, l, mid, i << 1));
}
if (R > mid) {
result = max(result, querymax(L, R, mid + 1, r, i << 1 | 1));
}
return result;
}
int querymin(int L, int R, int l, int r, int i) {
if (L <= l && r <= R) {
return minn[i];
}
int mid = (l + r) >> 1;
int result = INT_MAX;
if (L <= mid) {
result = min(result, querymin(L, R, l, mid, i << 1));
}
if (R > mid) {
result = min(result, querymin(L, R, mid + 1, r, i << 1 | 1));
}
return result;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> c;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
build(1, 1, n);
for (int i = 1; i <= n - m + 1; i++) {
//cout<<i<<' '<<querymax(i, i + m - 1, 1, n, 1)<<' '<<querymin(i, i + m - 1, 1, n, 1)<<endl;
if ((querymax(i, i + m - 1, 1, n, 1) - querymin(i, i + m - 1, 1, n, 1)) <= c) {
cout << i << endl;
flag = false;
}
}
if (flag) {
cout << "NONE" << endl;
}
return 0;
}
由于线段树自带大常数,在cy的 老旧 机器跑不过,只能跑 \(93\) 分。但是在洛谷上是能 \(AC\) 的。
正解单调队列不打