Luogu P2572 [SCOI2010]序列操作(线段树)题解

细节狂魔题

题意:维护一个01序列,支持区间修改(全部变01),区间取反,区间求和,区间求最长1序列

做法

显然应该用线段树维护。

线段树需要维护:每段区间内0的数量,1的数量,最长1序列长度,最长0序列长度,从左端点开始的0/1序列长度,从右端点开始的0/1序列长度。很少吧

注意!在标记下传时会有先后,赋值标记会覆盖翻转标记,而翻转标记会影响赋值标记

为什么我觉得网上的题解都写的好复杂,其实区间修改与取反可以写在一起,求序列与求和也可以很方便地写出来。

代码

copy
#include <cstdio> #include <cmath> #include <algorithm> using namespace std; inline int read(){ int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9'){x = x * 10 + c - '0'; c = getchar();} return x * f; } const int maxn = 1e5 + 10; int n,m; struct Seg_Tree{ #define lc(x) x << 1 #define rc(x) x << 1 | 1 struct node{ int c,c0,sum,lm,lm0,rm,rm0,tag,re; }t[maxn << 2]; // c->num of continued 1; c0->num of continued 0; void update(int l, int r, int p){ int mid = (l + r) >> 1; t[p].sum = t[rc(p)].sum + t[lc(p)].sum; t[p].c = max( max(t[lc(p)].c, t[rc(p)].c), t[lc(p)].rm + t[rc(p)].lm ); t[p].c0 = max( max(t[lc(p)].c0, t[rc(p)].c0), t[lc(p)].rm0 + t[rc(p)].lm0 ); if(t[lc(p)].lm == mid - l + 1) t[p].lm = mid - l + 1 + t[rc(p)].lm; else t[p].lm = t[lc(p)].lm; if(t[rc(p)].rm == r - mid) t[p].rm = r - mid + t[lc(p)].rm; else t[p].rm = t[rc(p)].rm; if(t[lc(p)].lm0 == mid - l + 1) t[p].lm0 = mid - l + 1 + t[rc(p)].lm0; else t[p].lm0 = t[lc(p)].lm0; if(t[rc(p)].rm0 == r - mid) t[p].rm0 = r - mid + t[lc(p)].rm0; else t[p].rm0 = t[rc(p)].rm0; } void build(int l, int r, int p){ t[p].tag = 0; if(l == r){ t[p].lm = t[p].rm = t[p].c = t[p].sum = read(); t[p].lm0 = t[p].rm0 = t[p].c0 = t[p].c ^ 1; return; } int mid = (l + r) >> 1; build(l, mid, lc(p)); build(mid + 1, r, rc(p)); update(l, r, p); } void f(int l, int r, int p, int typ){ //1 -> to 0; 2 -> to 1; 3 -> to ~ if(typ == 1){ t[p].sum = t[p].c = t[p].lm = t[p].rm = 0; t[p].c0 = t[p].lm0 = t[p].rm0 = r - l + 1; t[p].tag = 1; t[p].re = 0; } if(typ == 2){ t[p].sum = t[p].c = t[p].lm = t[p].rm = r - l + 1; t[p].c0 = t[p].lm0 = t[p].rm0 = 0; t[p].tag = 2; t[p].re = 0; } if(typ == 3){ t[p].sum = r - l + 1 - t[p].sum; swap(t[p].c0, t[p].c); swap(t[p].lm, t[p].lm0); swap(t[p].rm, t[p].rm0); if(t[p].tag == 1) t[p].tag = 2; else if(t[p].tag == 2) t[p].tag = 1; else t[p].re ^= 1; } } void downdate(int l, int r, int p){ if(t[p].tag){ int mid = (l + r) >> 1; f(l, mid, lc(p), t[p].tag); f(mid + 1, r, rc(p), t[p].tag); t[p].tag = 0; t[p].re = 0; } if(t[p].re){ int mid = (l + r) >> 1; f(l, mid, lc(p), 3); f(mid + 1, r, rc(p), 3); t[p].re = 0; } } void change(int L, int R, int l, int r, int p, int typ){ if(L <= l && R >= r){ f(l, r, p, typ); return; } downdate(l, r, p); int mid = (l + r) >> 1; if(mid >= L) change(L, R, l, mid, lc(p), typ); if(mid < R) change(L, R, mid + 1, r, rc(p), typ); update(l, r, p); } int query(int L, int R, int l, int r, int p){ //get the num of continued 1 if(L <= l && R >= r) return t[p].c; downdate(l, r, p); int mid = (l + r) >> 1; int ans = 0; if(mid >= L) ans = query(L, R, l, mid, lc(p)); if(mid < R) ans = max(ans, query(L, R, mid + 1, r, rc(p))); if(mid >= L && mid < R) ans = max(ans, min(t[lc(p)].rm, mid - L + 1) + min(t[rc(p)].lm, R - mid)); return ans; } int get_sum(int L, int R, int l, int r, int p){ if(L <= l && R >= r) return t[p].sum; downdate(l, r, p); int mid = (l + r) >> 1; int ans = 0; if(mid >= L) ans += get_sum(L, R, l, mid, lc(p)); if(mid < R) ans += get_sum(L, R, mid + 1, r, rc(p)); return ans; } }tree; int main(){ n = read(), m = read(); tree.build(1, n, 1); for(int i = 1; i <= m; ++ i){ int opt = read(), x = read(), y = read(); x += 1, y += 1; if(opt == 0) tree.change(x, y, 1, n, 1, 1); if(opt == 1) tree.change(x, y, 1, n, 1, 2); if(opt == 2) tree.change(x, y, 1, n, 1, 3); if(opt == 3) printf("%d\n", tree.get_sum(x, y, 1, n, 1)); if(opt == 4) printf("%d\n", tree.query(x, y, 1, n, 1)); } return 0; }

真是一点也不难写呢

总结

这类涉及带修区间求子序列的题基本上都是这种思路,考虑三种情况:答案序列在左边,答案序列在右边,答案序列横跨mid,然后就可以很方(nan)便(tiao)地用线段树维护了。

难想的主要是:update函数,downdate函数

posted @   When_C  阅读(112)  评论(0编辑  收藏  举报
编辑推荐:
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
阅读排行:
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法
· Vite CVE-2025-30208 安全漏洞
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起