最近觉得线段树比较可爱~
所以就鸽着吧
知识来源:https://www.luogu.org/blog/yestoday/mao-shu
由于 对于我这样的一个蒟蒻线段树暂时还不可学,选择学习猫树
注:据知识来源表明,猫树难以用来区间修改
关于此数据结构的时空复杂度分析请参见知识来源
猫树支持维护满足结合律且支持快速合并的信息(摘自知识来源)
我的理解:
在小学课本中表述如下:
加法结合律:三个数相加,先把前面两个数相加,再加第三个数,或者先把后面两个数相加,再和第一个数相加,它们的和不变————360百科:结合律
关于另一个条件“快速合并”,再摘一段知识来源中的思想:以一维数据的线段查询为例,若可以将该区间分成两部分,且这两个部分预先都被处理过,且这两个区间可以合并成当前查询区间,就可以快速得到答案。
举个栗子
我们要维护区间和
可以将当前序列分成左右两部分,从中间点向左右遍历,得到某一点到中间点的区间和
这样,如果要查询的区间的左右端点分别在此区间的左右两端,那么就可以快速合并出这一段区间的区间和。
但是如果要查询的区间同时落在当前序列中间点的左边或右边呢?
那么可以将这一边的序列再次进行 二分 的序列维护。
那么问题来了,每次区间的二分维护,遍历出来的结果一定是与上一层的二分遍历出来的结果不同的(因为中间点不同),
就要根据递归深度确定每一层的存储位置,
可以想到,如果我们维护的序列长度不超过 1e6,那么只需要20层的预处理空间。
为什么呢?因为每一次对当前递归层的每一段进行二分,下一层得到的“段数”总是这一层的2倍,
当无法二分时,下一层也就不存在了。
而1e6长度的数列,最多有1e6个数(废话),2的20次幂为1048576,也就是说,对于长度为1048576的数列,二十层也够用,1e6的长度也不用说了。
代码细节请看知识来源的代码,在此就不抄了。
来自知识来源的猫树例题代码:
----------2019,5,17更新。
https://www.luogu.org/recordnew/show/19119205
不粘贴了,强行模仿码风,define好用啊qwq hhhhha
#include<iostream> #include<cstdio> using namespace std; const int MAXN = 50001; #define ls (ind<<1) #define rs (ind<<1|1) #define mid ((l+r)>>1) int a[MAXN<<1], s[21][MAXN<<2], p[21][MAXN<<2], pos[MAXN], lg[MAXN<<2]; inline int Max(int a, int b) { return (a > b ? a : b); } int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9') { x = x*10 + c - 48; c = getchar(); } return x*f; } void build(int ind, int l, int r, int d) { if(l == r) { pos[l] = ind; return; } int prep = a[mid], sm = a[mid]; sm = Max(sm,0); s[d][mid] = a[mid]; p[d][mid] = a[mid]; for(int i = mid-1; i >= l; --i) { sm += a[i], prep += a[i]; s[d][i] = Max(s[d][i+1],prep); p[d][i] = Max(p[d][i+1],sm); sm = Max(sm,0); } p[d][mid+1] = s[d][mid+1] = a[mid+1]; prep = sm = a[mid+1], sm = Max(0,sm); for(int i = mid+2; i <= r; ++i) { sm += a[i], prep += a[i]; s[d][i] = Max(s[d][i-1],prep); p[d][i] = Max(p[d][i-1],sm); sm = Max(sm,0); } build(ls, l, mid, d+1); build(rs, mid+1, r, d+1); } int query(int l, int r) { if(l == r) return a[l]; int d = lg[pos[l]] - lg[pos[l]^pos[r]]; return Max(Max(p[d][l],p[d][r]), s[d][l]+s[d][r]); } int main() { int n = read(); for(int i = 1; i <= n; ++i) a[i] = read(); int len = 2; while(len<n) len <<= 1; build(1, 1, len, 1); int l = len << 1; for(int i = 2; i <= l; ++i) lg[i] = lg[i>>1] + 1; int m = read(); for(int i = 1, l, r; i <= m; ++i) l = read(), r = read(), cout << query(l, r) << '\n'; return 0; }
Aya