Luogu P2572 [SCOI2010]序列操作(线段树)题解
细节狂魔题
题意:维护一个序列,支持区间修改(全部变或),区间取反,区间求和,区间求最长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;
}
真是一点也不难写呢
总结
这类涉及带修区间求子序列的题基本上都是这种思路,考虑三种情况:答案序列在左边,答案序列在右边,答案序列横跨,然后就可以很方(nan)便(tiao)地用线段树维护了。
难想的主要是:函数,函数
分类:
C++_数据结构
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法
· Vite CVE-2025-30208 安全漏洞