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\) 的。

正解单调队列不打

posted @ 2022-02-26 13:49  蒟蒻xiezheyuan  阅读(35)  评论(0编辑  收藏  举报