关于线段树的区间修改(加值), 区间查询(求和), 利用延迟化的思想, 是比较容易的.
主要是想好标记下传中lazy-tag的意义是什么, 这个思路中的add标记的意义是: 当前节点没有问题, 但是子节点要受到当前节点标记的影响.
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 600;
ll s[MAXN << 2 | 1], add_flag[MAXN << 2 | 1];
ll a[MAXN], v;
int ql, qh;
inline void down(int o, int lo, int hi){
if(add_flag[o]){// 我们保证了访问某个节点时, 他的祖先节点不存在任何标记
add_flag[o << 1] += add_flag[o];
add_flag[o << 1 | 1] += add_flag[o];
int mi = lo + hi >> 1;
s[o << 1] += (mi - lo) * add_flag[o];
s[o << 1 | 1] += (hi - mi) * add_flag[o];
add_flag[o] = 0;
}
}
inline void up(int o){
s[o] = s[o << 1] + s[o << 1 | 1];
}
void build(int o, int lo, int hi){
if(hi - lo < 2){
s[o] = a[lo];
return;
}
int mi = lo + hi >> 1;
build(o << 1, lo, mi);
build(o << 1 | 1, mi, hi);
up(o);
}
void modify(int o, int lo, int hi){
if(ql <= lo && hi <= qh){// o节点的sum已经修改了, 记录v值, 如果要访问子区间, 需要传递v值
s[o] += (hi - lo) * v;
add_flag[o] += v;
return;
}
if(qh <= lo || hi <= ql){
return;
}
down(o, lo, hi);
int mi = lo + hi >> 1;
modify(o << 1, lo, mi);
modify(o << 1 | 1, mi, hi);
up(o);
}
ll query(int o, int lo, int hi){
if(ql <= lo && hi <= qh){
return s[o];
}
if(qh <= lo || hi <= ql){
return 0;
}
down(o, lo, hi);
int mi = lo + hi >> 1;
ll rst = 0;
rst += query(o << 1, lo ,mi);
rst += query(o << 1 | 1, mi ,hi);
return rst;
}
int main(){
int N, Q;
scanf("%d%d", &N, &Q);
for(int i = 0; i < N;){
scanf("%lld", a + ++i);
}
++N;
build(1, 1, N);
char cmd;
while(Q--){
getchar();
scanf("%c", &cmd);
if(cmd == 'C'){
scanf("%d%d%lld", &ql, &qh, &v);
++qh;
modify(1, 1, N);
}
else{
scanf("%d%d", &ql, &qh);
++qh;
printf("%lld\n", query(1, 1, N));
}
}
return 0;
}
计蒜客-黑白石头
沙滩上有一些石头,石头的颜色是白色或者黑色。小羊会魔法,它能把连续一段的石头,黑色变成白色,白色变成黑色。小羊喜欢黑色,她想知道某些区间中最长的连续黑石头是多少个。
这个和算法竞赛入门经典里的动态区间最长连续子序列很像.
先用线段树维护每段区间, 从区间左/右端的黑/白石子的长度和区间内最长连续黑/白石子的长度, 一共6个信息.
在线段树中, 当前区间的最长连续黑色石头的长度, 要么完全在左孩子里, 要么完全在右孩子里, 要么就横跨左孩子和右孩子.
如果横跨左孩子和右孩子, 这时就需要知道左孩子的右端点的黑色石头的长度, 同理右孩子.
白色石头同理.
查询的时候, 也是这么组织的. 因为查询区间可以在线段树上被分为连续的log(n)个区间, 按照线段树询问的方式, 俩俩合并就知道真的查询区间的最长的连续黑石头的个数了.
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cmath>
using namespace std;
const int MAXN = 1e5 + 600;
struct SGT_NODE{
int rev;
int max0, max1, left0, left1, right0, right1;
} s[MAXN << 2 | 1];
struct Q_NODE{
int max, left, right;
Q_NODE(int max, int left, int right): max(max), left(left), right(right){}
};
int ql, qh, v;
inline void change(int o){
swap(s[o].max0, s[o].max1);
swap(s[o].left0, s[o].left1);
swap(s[o].right0, s[o].right1);
}
inline void down(int o, int lo, int hi){
s[o].rev %= 2;
if(s[o].rev){
s[o << 1].rev += s[o].rev;
s[o << 1 | 1].rev += s[o].rev;
change(o << 1);// 要注意这里, 这两个子节点是必须要反转的
change(o << 1 | 1);
s[o].rev = 0;
}
}
inline void up(int o, int lo, int hi){
int mi = (lo + hi) >> 1;
int left = o << 1, right = o << 1 | 1;
int leftSize = mi - lo, rightSize = hi - mi;
s[o].max0 = max(s[left].right0 + s[right].left0, max(s[left].max0, s[right].max0));
s[o].max1 = max(s[left].right1 + s[right].left1, max(s[left].max1, s[right].max1));
s[o].left0 = s[left].left0 == leftSize? leftSize + s[right].left0: s[left].left0;
s[o].left1 = s[left].left1 == leftSize? leftSize + s[right].left1: s[left].left1;
s[o].right0 = s[right].right0 == rightSize? s[left].right0 + rightSize: s[right].right0;
s[o].right1 = s[right].right1 == rightSize? s[left].right1 + rightSize: s[right].right1;
}
void build(int o, int lo, int hi){
if(hi - lo < 2){
scanf("%d", &v);
if(v){
s[o].max1 = s[o].left1 = s[o].right1 = 1;
s[o].max0 = s[o].left0 = s[o].right0 = 0;
}
else{
s[o].max0 = s[o].left0 = s[o].right0 = 1;
s[o].max1 = s[o].left1 = s[o].right1 = 0;
}
return;
}
int mi = (lo + hi) >> 1;
build(o << 1, lo, mi);
build(o << 1 | 1, mi, hi);
up(o, lo, hi);
}
void modify(int o, int lo, int hi){
if(ql <= lo && hi <= qh){
++s[o].rev;
swap(s[o].max0, s[o].max1);
swap(s[o].left0, s[o].left1);
swap(s[o].right0, s[o].right1);
return;
}
if(qh <= lo || hi <= ql){
return;
}
down(o, lo, hi);
int mi = (lo + hi) >> 1;
modify(o << 1, lo, mi);
modify(o << 1 | 1, mi, hi);
up(o, lo, hi);
}
Q_NODE query(int o, int lo, int hi){
if(ql <= lo && hi <= qh){
return Q_NODE(s[o].max1, s[o].left1, s[o].right1);
}
if(qh <= lo || hi <= ql){
return Q_NODE(0, 0, 0);
}
down(o, lo, hi);
int mi = (lo + hi) >> 1;
Q_NODE left = query(o << 1, lo, mi), right = query(o << 1 | 1, mi, hi);
int leftSize = mi - lo, rightSize = hi - mi;
Q_NODE rst(max(left.right + right.left, max(left.max, right.max)), 0, 0);
rst.left = left.left == leftSize? leftSize + right.left: left.left;
rst.right = right.right == rightSize? left.right + rightSize: right.right;
return rst;
}
int main(){
int N;
scanf("%d", &N);
++N;
build(1, 1, N);
int q;
scanf("%d", &q);
while(q--){
scanf("%d%d%d", &v, &ql, &qh);
++qh;
if(v){
modify(1, 1, N);
}
else{
printf("%d\n", query(1, 1, N).max);
}
}
return 0;
}