zkw线段树具体内容请百度统计的力量(这是他讲的时候所用的ppt的名字)
今天我们就来完整的写一个zkw线段树。
正如他在ppt里讲的
至于区间查询,由于每个点存的内容都和它的儿子没有关系,所以某种意义上我们可以直接找到区间的边界点,然后开始一层一层向上累加去个最大or最小就可以了。但是这个找边界点的过程还会浪费点地方。不如直接就从边界的叶子节点处一层一层向上累加,每到一层,都注意去最大or最小,来维护答案。
然后就是他的ppt中的错误,第一处在add函数里,它没有上浮到根节点,这样的话单就下次查询来说,并不会造成什么(你可以试试,这是从某点一直到根的路径中的sum仍为这个点存的Max),但是,如果有了另一次更新,这时,就会出现问题(出个数据模拟一下就可以)。所以在add函数的尾部还要加一部上浮到根节点的语句。
第二处在Query函数中,如果这时查询的不是区间是点,这时会有l=r,进一次循环,你发现l=r=0,这时,很明显,函数最后的那步上浮到根节点的循环是出不去了。
第三处仍在Query函数中,那就是这时的查询不再是开区间了,而是闭区间。比如区间的左节点是某个点的右儿子,那么这时的开区间头便是某个点的左儿子。这时按照zkw的惯例,下次的开区间节点就是他的父亲了,但是由于他的父亲的儿子中有区间内的点,从而这个父亲也算区间内的点,那么这时会出现了矛盾,也就是说,本来要经历的点却没有经历。这样肯定就有问题了,所以我们只能闭区间查询,这样才不会发生上浮一层之后,点与区间的位置关系发生改变。
好了,这次的总结就差不多了。这里是cf上的一个题目,考的就是这个。底下附上我的代码,可以看一下细节部分。
#include <bits/stdc++.h> #define up(i, lower, upper) for(int i = lower; i < upper; i++) #define down(i, lower, upper) for(int i = upper-1; i >= lower; i--) using namespace std; typedef pair<int, int> pii; typedef pair<double, double> pdd; typedef vector<int> vi; typedef vector<pii> vpii; typedef __int64 ll; typedef unsigned __int64 ull; const double pi = acos(-1); const double eps = 1.0e-9; template<class T> inline char read(T &n){ T x = 0, tmp = 1; char c = getchar(); while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar(); if(c == '-') c = getchar(), tmp = -1; while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar(); n = x*tmp; return c; } template <class T> inline void write(T n) { if(n < 0) { putchar('-'); n = -n; } int len = 0,data[20]; while(n) { data[len++] = n%10; n /= 10; } if(!len) data[len++] = 0; while(len--) putchar(data[len]+48); } //--------------------------------------------------------- struct SegTree { ll tree[550000]; int m; void build(int len) { m = 1; memset(tree, 0, sizeof tree); while(m-1 <= len) m*=2; up(i, 1, len+1) read(tree[i+m]); down(i, 1, m) tree[i] = min(tree[(i<<1)], tree[(i<<1)+1]); down(i, 1, len+m+1) tree[i] = tree[i] - tree[i>>1]; } void update(int l, int r, int val) { ll tmp; for(l += m-1, r += m+1; l^r^1; l>>=1, r>>=1) { if(~l&1) tree[l^1]+=val; if(r&1) tree[r^1]+=val; tmp = min(tree[l], tree[l^1]), tree[l] -= tmp, tree[l^1] -= tmp, tree[l>>1] += tmp; tmp = min(tree[r], tree[r^1]), tree[r] -= tmp, tree[r^1] -= tmp, tree[r>>1] += tmp; } for (;l!=1;l>>=1) tmp = min(tree[l],tree[l^1]), tree[l] -= tmp, tree[l^1] -= tmp, tree[l>>1] += tmp; } ll query(int l, int r) { ll lAns = 0, rAns = 0; l+=m, r+=m; if(l != r) { for(; l^r^1; l>>=1, r>>=1) { lAns += tree[l], rAns += tree[r]; if(~l&1) lAns = min(lAns, tree[l^1]); if(r&1) rAns = min(rAns, tree[r^1]); } } ll ans = min(lAns+tree[l], rAns+tree[r]); while(l > 1) ans += tree[l>>=1]; return ans; } }; SegTree a; int main() { int len, n, m, l, r; read(len); a.build(len); read(n); while(n--) { read(l); if(read(r) != '\n') { read(m); if(l > r) a.update(l+1, len, m), a.update(1, r+1, m); else a.update(l+1, r+1, m); } else { if(l > r) write(min(a.query(l+1, len), a.query(1, r+1))); else write(a.query(l+1, r+1)); puts(""); } } return 0; }