[ABC341E] Alternating String 题解

题目传送门

原题传送门

题意

给出长为 \(n\)01 串,如果一个子串 01 交替出现,则称其为“好的”。有 \(q\) 次询问,把 \([x,y]\) 中的每一位反转或者询问 \([x,y]\) 是否是“好的”。

分析

一眼线段树。

用线段树维护区间是否是“好的”,每个节点维护最左段和最右端的值,pushupquery 向上合并区间时判断即可。

下面的代码有比较详细的注释,具体看代码即可,看不懂可以画图辅助理解。

代码

#include <bits/stdc++.h>
#define int long long
#define N 500005
using namespace std;
int n, q, a[N];

inline int read(int &x) {
	char ch = x = 0;
	int m = 1;
	while (ch < '0' || ch > '9') {
		ch = getchar();
		if (ch == '-') m *= -1;
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 1) + (x << 3) + ch - 48;
		ch = getchar();
	}
	x *= m;
	return x;
}

struct node {
	int l, r, lval, rval, tag, isg;
};

struct segtre {
	node tre[N << 1]; //动态开点节省空间
	int root, tot;
	inline void pushup(int rt) { //合并两个区间
		int l = tre[rt].l, r = tre[rt].r;
		tre[rt].isg = (tre[l].rval ^ tre[r].lval) & tre[l].isg & tre[r].isg; //左右两个区间都是好的并且左区间右侧和右区间左侧不同时当前的区间才是好的
		tre[rt].lval = tre[l].lval;
		tre[rt].rval = tre[r].rval;
		return ;
	}
	inline void pushdown(int rt) { //下传反转标记
		if (!tre[rt].tag) return ;
		int l = tre[rt].l, r = tre[rt].r;
		tre[l].tag ^= 1, tre[r].tag ^= 1;
		tre[l].lval ^= 1, tre[l].rval ^= 1;
		tre[r].lval ^= 1, tre[r].rval ^= 1; //全都反转
		tre[rt].tag = 0;
		return ;
	}
	void build(int &rt, int l, int r) { //建树
		if (!rt) rt = ++tot;
		if (l == r) {
			tre[rt].lval = tre[rt].rval = a[l]; //底层初值
			tre[rt].isg = 1; //长度为 1 的区间应该是好的
			return ;
		}
		int mid = (l + r) >> 1;
		build(tre[rt].l, l, mid);
		build(tre[rt].r, mid + 1, r);
		pushup(rt);
		return ;
	}
	void update(int rt, int l, int r, int L, int R) {
		if (L <= l && r <= R) { //包含就打上 tag 返回
			tre[rt].tag ^= 1;
			tre[rt].lval ^= 1;
			tre[rt].rval ^= 1;
			return ;
		}
		pushdown(rt);
		int mid = (l + r) >> 1;
		if (L <= mid) update(tre[rt].l, l, mid, L, R);
		if (R > mid) update(tre[rt].r, mid + 1, r, L, R);
		pushup(rt);
		return ;
	}
	int query(int rt, int l, int r, int L, int R) {
		if (L <= l && r <= R) {
			return tre[rt].isg;
		}
		pushdown(rt);
		int mid = (l + r) >> 1, lr = tre[rt].l, rr = tre[rt].r;
		if (L <= mid && R > mid) { //如果跨越 mid 就要合并左右两个询问区间,合并可以参考 pushup 函数里的注释
			if ((tre[lr].rval ^ tre[rr].lval) == 0) return 0;
			return query(lr, l, mid, L, R) && query(rr, mid + 1, r, L, R);
		} else if (L <= mid) { //没有跨越就直接返回就好了
			return query(lr, l, mid, L, R);
		} else {
			return query(rr, mid + 1, r, L, R);
		}
	}
} tre;

signed main() {
	read(n), read(q);
	char ch = 0;
	while (ch != '0' && ch != '1') ch = getchar();
	for (int i = 1; i <= n; i++, ch = getchar()) {
		if (ch == '1') a[i] = 1;
	}
	tre.build(tre.root, 1, n);
	int opt, l, r;
	while (q--) {
		read(opt), read(l), read(r);
		if (opt == 1) {
			tre.update(tre.root, 1, n, l, r);
		} else {
			if (tre.query(tre.root, 1, n, l, r)) {
				printf("Yes\n");
			} else {
				printf("No\n");
			}
		}
	}
	return 0;
}
posted @ 2024-02-27 19:51  wswwhcs  阅读(3)  评论(0编辑  收藏  举报