關於線段樹的區間修改(加值), 區間查詢(求和), 利用延遲化的思想, 是比較容易的.
主要是想好標記下傳中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;
}