[ABC341E] Alternating String 题解
题意
给出长为 01
串,如果一个子串 01
交替出现,则称其为“好的”。有
分析
一眼线段树。
用线段树维护区间是否是“好的”,每个节点维护最左段和最右端的值,pushup
和 query
向上合并区间时判断即可。
下面的代码有比较详细的注释,具体看代码即可,看不懂可以画图辅助理解。
代码
#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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话