要求维护一段长度为 \(n\) 的静态序列的区间最大子段和。
有 \(m\) 次询问,每次询问输出区间 \([L,R]\) 的最大子段和。
\(|a[i]| \leq 15007\),\(1 \leq m,n\leq5\times10^4\)
首先想到如果用线段树的方法,那么预处理时间复杂度为\(O(n)\),总询问复杂度为\(O(m\cdot logn)\)。
当然这么想在这一题中没问题,但是如\(m\)的范围更大一点呢?比如\(1 \leq m \leq 1 \times10^7\)。这时候如果用线段树,很有可能会\(TLE\)。
既然没有修改,那么可以用\(ST\)表或者猫树这种 \(O(1)\) 查询的数据结构来完成这一题,于是笔者选择用猫树来完成这一题。
因为ST表太丑了(doge)
总时间复杂度为\(O(n\cdot logn+m)\)
注释在代码里
#include <cstdio> #include <algorithm> #include <ctype.h> #define reg register using namespace std; namespace io { template<typename T>inline void read(T &x) { char f=0,ch; x = 0; while(!isdigit(ch=getchar())) f |= ch == '-'; while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar(); x = f? -x: x; } char ch[20]; template<typename T>inline void write(T x) { (x < 0) && (x = -x, putchar('-')); x || putchar('0'); reg int i=0; while(x) ch[i++] = x % 10 ^ 48, x /= 10; while(i) putchar(ch[--i]); } }//快读快写 #define rd io::read #define wt io::write const int maxN = 100020; struct CatTree { int pre, sum; }t[maxN << 1][22];//维护一下区间前缀/后缀最大值,用于合并,然后维护一下区间子串最大值 int d[maxN << 1], pos[maxN << 1], a[maxN << 1]; int len=2, n, m; void init(int, int, int); int query(int, int); int main() { freopen("1.in", "r", stdin); freopen("1.out", "w", stdout); rd(n); while(len < n) len <<= 1; for(reg int i = 1; i <= n; ++i) rd(a[i]); for(reg int i = 1; i <= len << 1; ++i) d[i] = d[i >> 1] + 1; init(1, 1, len); rd(m); for(reg int i = 1, l, r; i <= m; ++i) { rd(l); rd(r); printf("%d\n", query(l, r)); } return 0; } void init(int k, int l, int r) { if (l==r) {pos[l]=k;return;} int mid = l + r >> 1, pre , str; pre = str = t[mid][d[k]].sum = t[mid][d[k]].pre = a[mid]; str = max(str, 0); for(reg int i = mid - 1; i >= l; --i) { pre += a[i], str += a[i]; t[i][d[k]].sum = max(t[i + 1][d[k]].sum, str); t[i][d[k]].pre = max(t[i + 1][d[k]].pre, pre);str = max(str, 0); }//处理左边区间子串最大值和左边区间后缀最大值。 pre = str = t[mid + 1][d[k]].sum = t[mid + 1][d[k]].pre = a[mid + 1]; str = max(str, 0); for(reg int i = mid + 2; i <= r; ++i) { pre += a[i], str += a[i]; t[i][d[k]].sum = max(t[i - 1][d[k]].sum, str); t[i][d[k]].pre = max(t[i - 1][d[k]].pre, pre);str = max(str, 0); } init(k << 1, l, mid);init(k << 1 | 1, mid + 1, r); } int query(int l, int r) { if (l == r) return a[l]; int fa = d[pos[l]] - d[pos[l] ^ pos[r]];//找到x和y的LCA return max(max(t[l][fa].sum, t[r][fa].sum), t[l][fa].pre + t[r][fa].pre); } //最后答案就等于Max{左边的区间子串最大值,右边的区间子串最大值,左边的后缀最大值+右边的前缀最大值}