骑士比武 题解

由于此题是某校 Online Judge 里面的题目,所以只给出题意。

题意

现在有一个长度为 n n n 的序列,一开始都为 0 0 0。有 m m m 次操作,每一次操作有三个参数 l l l r r r x x x,保证 l ≤ x ≤ r l\le x \le r lxr。对于所有满足 l ≤ i ≤ r l\le i\le r lir i i i,且 i ≠ x i \not =x i=x,如果 a i a_i ai 不为 0 0 0,则将 a i a_i ai 替换成 x x x

做法

我们可以用线段树解决这个区间问题,对于每一个线段树节点,如果这个节点所覆盖的一个区间全部是同一个数,用一个 tag 变量存储这个数字,如果这个节点所覆盖的区间全部是非零数,就用一个 vis 变量存储,有零就将 vis 设为 0 0 0,否则为 1 1 1。我们用一个 same 变量存储这个节点的左儿子所覆盖的区间的数是否等于右儿子所覆盖的区间。如果这个区间的数不一样, same 就是 0 0 0。一开始这三个变量的初值分别为 0 0 0 0 0 0 1 1 1

节点维护值

让我们思考一下这三个变量怎么维护:我们有一个节点 p,然后我们要维护 p 的这三个变量。

如果 p 的左儿子的 visp 的右儿子的 vis 都是 1 1 1,说明左边的区间和右边的区间都没有 0 0 0pvis 自然应该设为 1 1 1,否则,就是 0 0 0。如果 p 的左儿子的 tag 不等于 p 右儿子的 tag,说明左儿子覆盖的区间和右儿子覆盖的区间数不一样,psame 就是 0 0 0,否则,不一定是 1 1 1。因为如果这个区间的数不一样,我们的 tag 就是 0。如果左儿子的区间和右儿子的区间都是乱的,而它们的 tag 都是 0 0 0。否则,ptag 就应该更新为儿子的 tag

操作

然后我们思考如何写修改和查询操作,这个题中,我们只用考虑单点的查询。

修改操作

首先,如果这个节点有 tag,那么传递 tag 给子节点,再更新自己的值。如果这个区间被修改区间包含而且这个区间没被修改过(tag 0 0 0),而且这个区间值都是 0 0 0vis 0 0 0),就直接将这个区间的 tag 设为我们要修改的东西。传递 tag,然后更新这个区间的值,就可以返回了。否则,如果这个区间全是 1 1 1vis 1 1 1),就不用改了,直接返回。然后修改儿子,更新数据。

查询操作

如果这个节点有 tag,就传掉,不要影响其他节点。如果已经递归到最底层的节点了,就返回这个节点的 tag。如果这个区间不包含答案所在位置,直接返回无穷大,然后再子区间的询问中取最小值就行了。

AC Code:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
int n, m;
struct node{
	int l, r, vis, tag, same;
};
node t[2400100];
void update(int p) {
	if (t[p].l == t[p].r && t[p].tag) {
		t[p].vis = 1; return;
	}
	else t[p].vis = t[p * 2].vis && t[p * 2 + 1].vis;
	if (t[p * 2].tag != t[p * 2 + 1].tag) t[p].same = 0;
	else t[p].tag = t[p * 2].tag;
}
void pushtag(int p) {
	if (!t[p * 2].tag) t[p * 2].tag = t[p].tag, t[p * 2].vis = 1;
	if (!t[p * 2 + 1].tag) t[p * 2 + 1].tag = t[p].tag, t[p * 2 + 1].vis = 1;
}
void maketree(int l, int r, int p) {
	t[p].l = l;
	t[p].r = r;
	if (l < r) {
		maketree(l, (l + r) / 2, p * 2);
		maketree((l + r) / 2 + 1, r, p * 2 + 1);
	}
	t[p].same = 1;
	update(p);
}
void change(int l, int r, int p, int x) {
	if (t[p].tag) {
		pushtag(p);
		update(p);
	}
	if (l <= t[p].l && t[p].r <= r && !t[p].tag && !t[p].vis) {
		t[p].tag = x;
		t[p].vis = 1;
		pushtag(p);
		update(p);
		return;
	}
	if (t[p].vis) return;
	if (l > t[p].r || r < t[p].l) {
		return;
	}
	change(l, r, p * 2, x);
	change(l, r, p * 2 + 1, x);
	update(p);
}
int get(int i, int p) {
	if (t[p].tag) {
		pushtag(p);
		update(p);
	}
	if (t[p].l == i && t[p].r == i) return t[p].tag;
	if (t[p].l > i || t[p].r < i) return 0x3f3f3f3f;
	return min(get(i, p * 2), get(i, p * 2 + 1));
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	maketree(1, n, 1);
	while (m--) {
		int l, r, x;
		cin >> l >> r >> x;
		if (x > l) change(l, x - 1, 1, x);
		if (x < r) change(x + 1, r, 1, x);
	}
	for (int i = 1; i <= n; i++) cout << get(i, 1) << ' ';
	return 0;
}
  • 29
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值