题解:序列操作

比较综合的 ds 题,综合了线段树常见的几种操作:维护最大子段和、区间翻转、区间求和、区间覆盖 。

维护子段和常见的我们维护三类东西:

前缀最长连续段、后缀最长连续段、当前区间上的最大子段和。

在 pushUp 时,对于一个区间的前后缀最值首先等于左右子树的最长前后缀,如果填满了一棵子树以后会得到:\(pre_{curr}=pre_{left}+pre_{right}\),当 \(pre_{left}==len_{left}\) 就会加上右子树的前缀最值。后缀同理,而答案显然为左右区间的答案加上上图中绿色的 \(suf_{left}+pre_{right}\) 三者中取最值。

现在,来考虑下每种标记和操作之间的影响:

  1. 对于覆盖标记,如果前面已经有翻转标记了,显然翻转标记需要直接清空。

  2. 对于翻转标记和最大子段和的标记,由于只涉及到 \(0/1\) 的翻转,所以我们在维护一组最大子段和标记,其中 \(0\) 是作为贡献的存在,即前缀最长的 \(0\),后缀最长的 \(0\),当前区间最大的 \(0\) 的段。

前后缀受到翻转影响,就直接交换两组标记即可,而最长子段和显然变成了最长 \(0\) 段和。剩余的就是常规的最大子段和查询等常规操作了,不熟的可以去温习下,关于最大子段和的查询实际上是维护一个线段树节点不断地 \(merge\) 答案节点区间形成最终答案节点,状态量的合并。剩余见代码注释即可。

参照代码
#include <bits/stdc++.h>

// #pragma GCC optimize("Ofast,unroll-loops")
// #pragma GCC optimize(2) #define isPbdsFile #ifdef isPbdsFile #include <bits/extc++.h> #else #include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/trie_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/list_update_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/exception.hpp>
#include <ext/rope> #endif using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef tuple<int, int, int> tii;
typedef tuple<ll, ll, ll> tll;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef __int128 i128;
#define hash1 unordered_map
#define hash2 gp_hash_table
#define hash3 cc_hash_table
#define stdHeap std::priority_queue
#define pbdsHeap __gnu_pbds::priority_queue
#define sortArr(a, n) sort(a+1,a+n+1)
#define all(v) v.begin(),v.end()
#define yes cout<<"YES"
#define no cout<<"NO"
#define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout);
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forv(i, a, b) for(int i=a;i>=b;i--)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define endl '\n'
//用于Miller-Rabin
[[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}; template <typename T>
int disc(T* a, int n)
{
return unique(a + 1, a + n + 1) - (a + 1);
} template <typename T>
T lowBit(T x)
{
return x & -x;
} template <typename T>
T Rand(T l, T r)
{
static mt19937 Rand(time(nullptr));
uniform_int_distribution<T> dis(l, r);
return dis(Rand);
} template <typename T1, typename T2>
T1 modt(T1 a, T2 b)
{
return (a % b + b) % b;
} template <typename T1, typename T2, typename T3>
T1 qPow(T1 a, T2 b, T3 c)
{
a %= c;
T1 ans = 1;
for (; b; b >>= 1, (a *= a) %= c)if (b & 1)(ans *= a) %= c;
return modt(ans, c);
} template <typename T>
void read(T& x)
{
x = 0;
T sign = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')sign = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
x *= sign;
} template <typename T, typename... U>
void read(T& x, U&... y)
{
read(x);
read(y...);
} template <typename T>
void write(T x)
{
if (typeid(x) == typeid(char))return;
if (x < 0)x = -x, putchar('-');
if (x > 9)write(x / 10);
putchar(x % 10 ^ 48);
} template <typename C, typename T, typename... U>
void write(C c, T x, U... y)
{
write(x), putchar(c);
write(c, y...);
} template <typename T11, typename T22, typename T33>
struct T3
{
T11 one;
T22 tow;
T33 three; bool operator<(const T3 other) const
{
if (one == other.one)
{
if (tow == other.tow)return three < other.three;
return tow < other.tow;
}
return one < other.one;
} T3() { one = tow = three = 0; } T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three)
{
}
}; template <typename T1, typename T2>
void uMax(T1& x, T2 y)
{
if (x < y)x = y;
} template <typename T1, typename T2>
void uMin(T1& x, T2 y)
{
if (x > y)x = y;
} constexpr int N = 1e5 + 10; struct Node
{
int sum;//区间和
int preMax, nxtMax, Max;//区间最大子段和的三个标记,前后缀最长和最大子段和
int len;//区间长度
int rev;//翻转标记
int cov;//覆盖标记,-1表示没有覆盖
int preZero, nxtZero;//前后缀最长0段
int MaxZero;//最长0段
Node() = default;
} node[N << 2]; #define sum(x) node[x].sum
#define preMax(x) node[x].preMax
#define nxtMax(x) node[x].nxtMax
#define Max(x) node[x].Max
#define len(x) node[x].len
#define rev(x) node[x].rev
#define cov(x) node[x].cov
#define preZero(x) node[x].preZero
#define nxtZero(x) node[x].nxtZero
#define MaxZero(x) node[x].MaxZero inline void push_up(const int curr)
{
sum(curr) = sum(ls(curr)) + sum(rs(curr));
preMax(curr) = preMax(ls(curr)), nxtMax(curr) = nxtMax(rs(curr));
if (preMax(curr) == len(ls(curr)))
preMax(curr) += preMax(rs(curr));
if (nxtMax(curr) == len(rs(curr)))
nxtMax(curr) += nxtMax(ls(curr));
Max(curr) = max({Max(ls(curr)),Max(rs(curr)),nxtMax(ls(curr)) + preMax(rs(curr))});
preZero(curr) = preZero(ls(curr)), nxtZero(curr) = nxtZero(rs(curr));
if (preZero(curr) == len(ls(curr)))
preZero(curr) += preZero(rs(curr));
if (nxtZero(curr) == len(rs(curr)))
nxtZero(curr) += nxtZero(ls(curr));
MaxZero(curr) = max({MaxZero(ls(curr)),MaxZero(rs(curr)),nxtZero(ls(curr)) + preZero(rs(curr))});
} //区间覆盖以后会影响标记
inline void Change(const int curr, const int val)
{
if (val)
{
sum(curr) = preMax(curr) = nxtMax(curr) = Max(curr) = len(curr);
preZero(curr) = nxtZero(curr) = MaxZero(curr) = 0;
}
else
{
preZero(curr) = nxtZero(curr) = MaxZero(curr) = len(curr);
sum(curr) = preMax(curr) = nxtMax(curr) = Max(curr) = 0;
}
}
//区间翻转就两组标记对换
inline void Rev(const int curr)
{
swap(preMax(curr),preZero(curr));
swap(nxtMax(curr),nxtZero(curr));
swap(Max(curr),MaxZero(curr));
sum(curr) = len(curr) - sum(curr);
}
//先考虑覆盖标记再考虑翻转标记
inline void push_down(const int curr)
{
if (cov(curr) != -1)
{
rev(ls(curr)) = rev(rs(curr)) = 0;
cov(ls(curr)) = cov(rs(curr)) = cov(curr);
Change(ls(curr),cov(curr));
Change(rs(curr),cov(curr));
cov(curr) = -1;
}
if (rev(curr))
{
rev(ls(curr)) ^= 1, rev(rs(curr)) ^= 1;
Rev(ls(curr)), Rev(rs(curr));
rev(curr) = 0;
}
} int n;
int a[N];
//建树
inline void Build(const int curr, const int l = 1, const int r = n)
{
len(curr) = r - l + 1;
cov(curr) = -1;
const int mid = l + r >> 1;
if (l == r)
{
Change(curr, a[l]);
return;
}
Build(ls(curr), l, mid);
Build(rs(curr), mid + 1, r);
push_up(curr);
}
//覆盖
inline void Cover(const int curr, const int l, const int r, const int val, const int s = 1, const int e = n)
{
const int mid = s + e >> 1;
if (l <= s and e <= r)
{
rev(curr) = 0;
cov(curr) = val;
Change(curr, val);
return;
}
push_down(curr);
if (l <= mid)Cover(ls(curr), l, r, val, s, mid);
if (r > mid)Cover(rs(curr), l, r, val, mid + 1, e);
push_up(curr);
}
//翻转
inline void Reverse(const int curr, const int l, const int r, const int s = 1, const int e = n)
{
const int mid = s + e >> 1;
if (l <= s and e <= r)
{
rev(curr) ^= 1;
Rev(curr);
return;
}
push_down(curr);
if (l <= mid)Reverse(ls(curr), l, r, s, mid);
if (r > mid)Reverse(rs(curr), l, r, mid + 1, e);
push_up(curr);
} //查询节点状态
inline Node Query(const int curr, const int l, const int r, const int s = 1, const int e = n)
{
const int mid = s + e >> 1;
if (l <= s and e <= r)return node[curr];
push_down(curr);
auto ans = Node();
if (r <= mid)ans = Query(ls(curr), l, r, s, mid);
else if (l > mid)ans = Query(rs(curr), l, r, mid + 1, e);
else
{
//合并状态量
const auto left = Query(ls(curr), l, r, s, mid);
const auto right = Query(rs(curr), l, r, mid + 1, e);
ans.sum = left.sum + right.sum;
ans.len = left.len + right.len;
ans.preMax = left.preMax;
ans.nxtMax = right.nxtMax;
if (ans.preMax == left.len)ans.preMax += right.preMax;
if (ans.nxtMax == right.len)ans.nxtMax += left.nxtMax;
ans.Max = max({left.Max, right.Max, left.nxtMax + right.preMax});
}
return ans;
} int q; inline void solve()
{
cin >> n >> q;
forn(i, 1, n)cin >> a[i];
Build(1);
while (q--)
{
int op, l, r;
cin >> op >> l >> r;
l++, r++;
if (op == 0 or op == 1)Cover(1, l, r, op);
else if (op == 2)Reverse(1, l, r);
else
{
auto ans = Query(1, l, r);
cout << (op == 3 ? ans.sum : ans.Max) << endl;
}
}
} signed int main()
{
// MyFile
Spider
//------------------------------------------------------
// clock_t start = clock();
int test = 1;
// read(test);
// cin >> test;
forn(i, 1, test)solve();
// while (cin >> n, n)solve();
// while (cin >> test)solve();
// clock_t end = clock();
// cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
}
\[时间复杂度为 \ O((n+m)\log{n})
\]

P2572 [SCOI2010] 序列操作 题解的更多相关文章

  1. 【题解】Luogu P2572 [SCOI2010]序列操作

    原题传送门:P2572 [SCOI2010]序列操作 这题好弱智啊 裸的珂朵莉树 前置芝士:珂朵莉树 窝博客里对珂朵莉树的介绍 没什么好说的自己看看吧 操作1:把区间内所有数推平成0,珂朵莉树基本操作 ...

  2. P2572 [SCOI2010]序列操作

    对自己 & \(RNG\) : 骄兵必败 \(lpl\)加油! P2572 [SCOI2010]序列操作 题目描述 lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要 ...

  3. 洛谷 P2572 [SCOI2010]序列操作

    题意简述 维护一个序列,支持如下操作 把[a, b]区间内的所有数全变成0 把[a, b]区间内的所有数全变成1 把[a,b]区间内所有的0变成1,所有的1变成0 询问[a, b]区间内总共有多少个1 ...

  4. BZOJ1858[Scoi2010]序列操作 题解

    题目大意: 有一个01序列,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0:1 a b 把[a, b]区间内的所有数全变成1:2 a b 把[a,b]区间 ...

  5. BZOJ1858:[SCOI2010]序列操作——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=1858 lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于 ...

  6. 洛谷P2572 [SCOI2010]序列操作(ODT)

    题解 题意 题目链接 Sol ODT板子题..... // luogu-judger-enable-o2 #include<bits/stdc++.h> #define LL long l ...

  7. Luogu P2572 [SCOI2010]序列操作 线段树。。

    咕咕了...于是借鉴了小粉兔的做法ORZ... 其实就是维护最大子段和的线段树,但上面又多了一些操作....QWQ 维护8个信息:1/0的个数(sum),左/右边起1/0的最长长度(ls,rs),整段 ...

  8. 洛谷$P2572\ [SCOI2010]$ 序列操作 线段树/珂朵莉树

    正解:线段树/珂朵莉树 解题报告: 传送门$w$ 本来是想写线段树的,,,然后神仙$tt$跟我港可以用珂朵莉所以决定顺便学下珂朵莉趴$QwQ$ 还是先写线段树做法$QwQ$? 操作一二三四都很$eas ...

  9. 洛谷P2572 [SCOI2010]序列操作

    线段树 pushdown写的很浪~ #include<cstdio> #include<cstdlib> #include<algorithm> #include& ...

  10. 洛谷P2572 [SCOI2010]序列操作(珂朵莉树)

    传送门 珂朵莉树是个吼东西啊 这题线段树代码4k起步……珂朵莉树只要2k…… 虽然因为这题数据不随机所以珂朵莉树的复杂度实际上是错的…… 然而能过就行对不对…… (不过要是到时候noip我还真不敢打… ...

随机推荐

  1. 2015年第六届 蓝桥杯B组 C/C++决赛题解

    1.积分之迷 小明开了个网上商店,卖风铃.共有3个品牌:A,B,C. 为了促销,每件商品都会返固定的积分. 小明开业第一天收到了三笔订单: 第一笔:3个A + 7个B + 1个C,共返积分:315 第 ...

  2. java获取年月日、时间与区间、Sql获取年月日区间

    SQL 获取时.日.周.月日期 因工作上常用到统计分析,需要用到具体的时间,故写于此 24小时: SELECT 0 AS hour UNION ALL SELECT 1 AS hour UNION A ...

  3. location对象的方法

    location.assign() 跟href一样,可以跳转页面(也称为重定向页面). location.replace() 替换当前页面,因为不记录历史,所以不能后退页面. location.rel ...

  4. node开发概述

    一.Node开发概述 1. 为什么要学习服务器端开发 能够与后端程序员更加紧密的配合 网站业务逻辑前置,学习前端技术需要后端技术支撑(ajax) 扩宽知识视野,能够站在更高的角度审视整个项目 2. 服 ...

  5. 小白学标准库之反射 reflect

    1. 反射简介 反射是 元编程 概念下的一种形式,它在运行时操作不同类型的对象,检查对象的类型,大小等信息,对于没有源代码的包反射尤其有用. 设想一个场景,读取一个包中变量 a 的类型,并打印该类型的 ...

  6. MySQL 覆盖索引详解

    本文转载自:MySQL 覆盖索引详解,作者 Sevn 1. 什么是索引? 索引(在 MySQL 中也叫"键key")是存储引擎快速找到记录的一种数据结构,通俗来说类似书本的目录. ...

  7. 网络要素服务(WFS)详解

    目录 1. 概述 2. GetCapabilities 3. DescribeFeatureType 4. GetFeature 4.1 Get访问方式 4.2 Post访问方式 5. Transac ...

  8. RabbitMQ .net core 客户端 EasyNetQ 的使用

    依赖注入 var connectionConfiguration = new ConnectionConfiguration { Hosts = new List<HostConfigurati ...

  9. linux环境C语言实现:h265与pcm封装成AVI格式

    ​ 前言 不知道是处于版权收费问题还是什么原因,H265现在也并没有非常广泛的被普及.将h265数据合成AVI的资料现在在网上也基本上没有.使用格式化工厂工具将h265数据封装成AVI格式,发现它在封 ...

  10. [转帖]TLS1.3 正式版发布 — 特性与开启方式科普

    https://cloud.tencent.com/developer/article/1376033 互联网工程指导委员会(IETF)释出了传输层安全性协议的最新版本 TLS 1.3.TLS 被广泛 ...