【权值线段树】JZOJ_3236 矮人排队

题意

给出 N N N个人的身高,有两种操作:
1 、 1、 1交换两人位置。
2 、 2、 2查询 a ∼ b a\sim b ab这些身高的人在序列中是否是一个连续子序列。

思路

一开始我用线段树记录当前区间的最大值和最小值,然后查询时就把在 a ∼ b a\sim b ab的区间拿出来,暴力判断一下是否连续,事实证明我是 ∗ ∗ **

其实要用线段树维护高度为 i i i的人位置在哪,以及区间最大最小值,在查询时,我们利用线段树判断一下当前区间最大位置-最小位置是否等于查询的人的总数。

代码

#include<cstdio>
#include<algorithm>

struct SegmentTree {
	int l, r, mins, maxs;
}t[800001];
int n, m, f;
int a[200001], h[200001];

void build(int p, int l, int r) {
	t[p].l = l;
	t[p].r = r;
	if (l == r) {
		t[p].mins = t[p].maxs = h[l];
		return;
	}
	int mid = l + r >> 1;
	build(p * 2, l, mid);
	build(p * 2 + 1, mid + 1, r);
	t[p].mins = std::min(t[p * 2].mins, t[p * 2 + 1].mins);
	t[p].maxs = std::max(t[p * 2].maxs, t[p * 2 + 1].maxs);
}

void change(int p, int pos, int v) {
	if (t[p].l == t[p].r) {
		t[p].mins = t[p].maxs = v;
		return;
	}
	int mid = t[p].l + t[p].r >> 1;
	if (pos <= mid) change(p * 2, pos, v);
	else change(p * 2 + 1, pos, v);
	t[p].mins = std::min(t[p * 2].mins, t[p * 2 + 1].mins);
	t[p].maxs = std::max(t[p * 2].maxs, t[p * 2 + 1].maxs);
}

std::pair<int, int> ask(int p, int l, int r) {
	if (l <= t[p].l && t[p].r <= r) return std::make_pair(t[p].mins, t[p].maxs);
	int mid = t[p].l + t[p].r >> 1;
	std::pair<int, int> res, res1, res2;
	if (l <= mid) res1 = ask(p * 2, l, r), res = res1;
	if (r > mid) res2 = ask(p * 2 + 1, l, r), res = l <= mid ? std::make_pair(std::min(res1.first, res2.first), std::max(res1.second, res2.second)) : res2;
	return res;
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]), h[a[i]] = i;
	build(1, 1, n);
	for (int op, x, y; m; m--) {
		scanf("%d %d %d", &op, &x, &y);
		if (op == 1) {
			change(1, a[x], h[a[y]]);
			change(1, a[y], h[a[x]]);
			std::swap(a[x], a[y]);
			std::swap(h[a[x]], h[a[y]]);
		}
		else {
			std::pair<int, int> res = ask(1, x, y);
			res.second - res.first == y - x ? printf("YES\n") : printf("NO\n");
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值