[ABC341E] Alternating String 题解

本文介绍了如何使用线段树数据结构解决区间修改和查询的问题,通过维护区间合法性、边界元素值以及懒标记,实现了区间更新和查询的有效操作,简化了复杂性。
摘要由CSDN通过智能技术生成

原题链接

思路简述

看到题目中有着区间修改,区间查询的要求,不难想到此题可以利用线段树通过。

首先,对于一个区间的合法性,反转后并不会改变;同时我们注意到,一个区间的合法性,只与它的两个子区间的合法性,以及左儿子区间最右边的值是否等于右儿子区间最左边的值有关。不难想到,我们的线段树上要维护的是这个区间是否合法,区间最左边的元素的值以及区间最右边的元素的值。

代码如下:

struct node {
    // 左端点,右端点,最左边元素的值,最右边元素的值,懒标记
	int l, r, lv, rv, tag; 
	bool islegal; //区间是否合法
} tree[N << 2];

那么,我们不难写出 pushup 函数:

inline void pushup(int p) {
	tree[p].lv = tree[ls].lv, tree[p].rv = tree[rs].rv;
    // 要求为两个子区间都合法,以及左儿子区间最右边的值不等于右儿子区间最左边的值
	if (tree[ls].islegal && tree[rs].islegal && (tree[ls].rv != tree[rs].lv)) tree[p].islegal = true;
	else tree[p].islegal = false;
	return ;
}

如果我们需要更新一个区间,我们只需要修改三个值:最左边元素的值,最右边元素的值,懒标记。那么,我们不难想到下放一个懒标记,也只需要更改这三个值即可。那么我们可以写出这样的 pushdown 函数以及 update 函数。

inline void pushdown(int p) {
	if (tree[p].tag) {
		tree[ls].tag ^= 1, tree[rs].tag ^= 1; // 下放标记
		tree[ls].lv ^= 1, tree[ls].rv ^= 1; // 更改区间左右端点元素值
		tree[rs].lv ^= 1, tree[rs].rv ^= 1;
		tree[p].tag = 0; // 清空懒标记
	}
	return ;
} 

void update(int p, int l, int r) {
	if (tree[p].l >= l && tree[p].r <= r) {
		tree[p].tag ^= 1;
		tree[p].lv ^= 1, tree[p].rv ^= 1;
		return ;
	}
	pushdown(p);
	int mid = (tree[p].l + tree[p].r) >> 1;
	if (l <= mid) update(ls, l, r);
	if (r > mid) update(rs, l, r);
	pushup(p);
	return ;
}

本题的查询方式比较复杂,不能通过两个子区间是否合法来判断区间是否合法,我们不妨直接返回一段区间,合并后判断这个区间的合法性。代码如下:

node query(int p, int l, int r) {
	if (tree[p].l >= l && tree[p].r <= r) return tree[p];
	pushdown(p);
	int mid = (tree[p].l + tree[p].r) >> 1;
	if (r <= mid) return query(ls, l, r); // 查询区间在左儿子下
	else if (l > mid) return query(rs, l, r); // 查询区间在右儿子下
	else { // 需要两端区间合并
		node queryl = query(ls, l, r), queryr = query(rs, l, r);
		node tmp;
		tmp.lv = queryl.lv, tmp.rv = queryr.rv;
		tmp.islegal = (queryl.islegal && queryr.islegal && (queryl.rv != queryr.lv)); // 类似于pushup函数的更新
		return tmp;
	}
}

这样的话,我们就能以一种简单的方式解决这个问题。

完整代码

赛时提交记录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值