P2572 [SCOI2010]序列操作
寫在前面
傻逼線段樹題,碼量第一次超 \(6.5k\)
因為不能考 \(NOIp\) 一氣之下把它肝了(放屁,你是因為想看小說,而做資料結構不用腦子,好在DJ來的時候方便掩飾
個人感覺理解本題後會對線段樹有更為深刻的理解,亦可增加對線段樹的套路用法,瞭解各操作之間的優先順序
本題解陳述儘量詳細周全,若有贅述的地方請自行跳過,如果有什麼疑問也可在評論區提出
簡述題意
給定一段 \(01\) 序列,有 \(5\) 種操作
0、一段區間變成 \(0\)
1、一段區間變成 \(1\)
2、對一段區間取反
3、詢問一段區間內有多少個 \(1\)
4、詢問一段區間內最多有多少個連續的 \(1\)
序列長為 \(n\)
Solution
操作 \(0,1,3\) 都比較好解決,線段樹基本操作,這裡不贅述
主要是 \(2,4\) 操作
對於 \(4\) 操作,我們不難想到可以用 \(max\) 維護區間最長的 \(1\) ,分別用 \(lmax\) 和 \(rmax\) 來維護左端點最長的 &1& 和右端點最長的 \(1\)
在上傳是注意 \(max\) 要和左右兒子的 \(max\) ,以及左兒子的 \(rmax\) 加右兒子的 \(lmax\) 這三個值中取最大值
(相信做過 GSS3 都能直接想到 \(4\) 的處理方法,沒做過也沒有關係,思路應該也是比較好理解的)
那加上 \(2\) 操作怎麼融合,另設一個懶標記 \(rev\) 來表示是否翻轉
1、因為有兩種修改操作,考慮懶標記優先順序:因為 \(0, 1\) 的操作會把取反操作覆蓋掉,所以覆蓋的優先順序要高於反轉的優先順序
2、只有一個 \(max,lmax,rmax\),每次翻轉後怎麼更新:直接設兩個不就好了,把 \(0, 1\) 的 \(max, lmax, rmax\) 都存下來,翻轉的時候只要交換就好了
3、翻轉後如何求和:這個比較簡單,用長度減掉原來的 \(sum\) 就好了
在詢問 \(4\) 操作時,要注意兩端序列的合併應使用結構體回傳(好像較為複雜的線段樹題都需要這樣處理)
ACcode
由於碼量較大,請注意保護眼睛
儘可能的做到了排版整齊,變數名稱清新易懂,過程簡潔明瞭,還附有一定的註釋來幫助理解
可以考慮用迴圈減少碼量,但我感覺拆開寫更方便理解(善用位運算可以省不少力)
/*
Work by: Suzt_ilymics
Knowledge: 傻逼線段樹
Time: O(??)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lson i << 1
#define rson i << 1 | 1
#define orz cout<<"lkp ak ioi"<<endl;
using namespace std;
const int MAXN = 1e5+5;
const int INF = 1;
const int mod = 1;
struct Tree{
int len, lazy, sum, lmax[2], rmax[2], max[2], rev;
}tree[MAXN << 2];
int n, m;
int a[MAXN];
int read(){
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
return s * w;
}
int max(int x, int y){ return x > y ? x : y; }
void push_up(int i){//上傳
tree[i].sum = tree[lson].sum + tree[rson].sum;//維護區間和
if(tree[lson].sum == tree[lson].len) tree[i].lmax[1] = tree[lson].lmax[1] + tree[rson].lmax[1];//維護
else tree[i].lmax[1] = tree[lson].lmax[1];
if(tree[rson].sum == tree[rson].len) tree[i].rmax[1] = tree[rson].rmax[1] + tree[lson].rmax[1];
else tree[i].rmax[1] = tree[rson].rmax[1];
tree[i].max[1] = max(tree[lson].max[1], tree[rson].max[1]);//維護區間最多有的連續的1,取左區間最大值、右區間最大值、左區間右邊最大值和右區間左邊最大值的和三個中的最大值
tree[i].max[1] = max(tree[i].max[1], tree[lson].rmax[1] + tree[rson].lmax[1]);
if(tree[lson].sum == 0) tree[i].lmax[0] = tree[lson].lmax[0] + tree[rson].lmax[0];//維護
else tree[i].lmax[0] = tree[lson].lmax[0];
if(tree[rson].sum == 0) tree[i].rmax[0] = tree[rson].rmax[0] + tree[lson].rmax[0];
else tree[i].rmax[0] = tree[rson].rmax[0];
tree[i].max[0] = max(tree[lson].max[0], tree[rson].max[0]);//維護區間最多有的連續的0,取左區間最大值、右區間最大值、左區間右邊最大值和右區間左邊最大值的和三個中的最大值
tree[i].max[0] = max(tree[i].max[0], tree[lson].rmax[0] + tree[rson].lmax[0]);
return ;
}
void build(int i, int l, int r){
tree[i].len = r - l + 1, tree[i].lazy = -1, tree[i].rev = 0;
if(l == r) {
tree[i].sum = a[l];
tree[i].max[0] = tree[i].lmax[0] = tree[i].rmax[0] = (a[l] == 0);
tree[i].max[1] = tree[i].lmax[1] = tree[i].rmax[1] = (a[l] == 1);
return ;
}
int mid = (l + r) >> 1;
build(lson, l, mid), build(rson, mid + 1, r);
push_up(i);
return ;
}
void push_down(int i){//下穿,注意優先順序
if(tree[i].lazy != -1){
tree[i].rev = 0;
int val = tree[i].lazy;
tree[lson].sum = tree[lson].len * val;
tree[rson].sum = tree[rson].len * val;
tree[lson].lazy = tree[rson].lazy = val;
tree[lson].rev = tree[rson].rev = 0;
tree[lson].lmax[val ^ 1] = tree[lson].rmax[val ^ 1] = tree[lson].max[val ^ 1] = 0;
tree[rson].lmax[val ^ 1] = tree[rson].rmax[val ^ 1] = tree[rson].max[val ^ 1] = 0;
tree[lson].lmax[val] = tree[lson].rmax[val] = tree[lson].max[val] = tree[lson].len;
tree[rson].lmax[val] = tree[rson].rmax[val] = tree[rson].max[val] = tree[rson].len;
tree[i].lazy = -1;
}
if(tree[i].rev){
tree[lson].sum = tree[lson].len - tree[lson].sum;
tree[rson].sum = tree[rson].len - tree[rson].sum;
if(tree[lson].lazy != -1) tree[lson].lazy ^= 1;
else tree[lson].rev ^= 1;
if(tree[rson].lazy != -1) tree[rson].lazy ^= 1;
else tree[rson].rev ^= 1;
swap(tree[lson].max[0], tree[lson].max[1]);
swap(tree[lson].lmax[0], tree[lson].lmax[1]);
swap(tree[lson].rmax[0], tree[lson].rmax[1]);
swap(tree[rson].max[0], tree[rson].max[1]);
swap(tree[rson].lmax[0], tree[rson].lmax[1]);
swap(tree[rson].rmax[0], tree[rson].rmax[1]);
tree[i].rev = 0;
}
return ;
}
void change01(int i, int l, int r, int L, int R, int k){//01覆蓋操作
push_down(i);
if(L <= l && r <= R){
tree[i].sum = tree[i].len * k;
tree[i].lazy = k;
tree[i].lmax[k] = tree[i].rmax[k] = tree[i].max[k] = tree[i].len;
tree[i].lmax[k ^ 1] = tree[i].rmax[k ^ 1] = tree[i].max[k ^ 1] = 0;
return ;
}
int mid = (l + r) >> 1;
if(mid < L) change01(rson, mid + 1, r, L, R, k);
else if(mid >= R) change01(lson, l, mid, L, R, k);
else change01(lson, l, mid, L, mid, k), change01(rson, mid + 1, r, mid + 1, R, k);
push_up(i);
}
void changef(int i, int l, int r, int L, int R){//取反操作
push_down(i);
if(L <= l && r <= R){
tree[i].sum = tree[i].len - tree[i].sum;
tree[i].rev ^= 1;
swap(tree[i].max[1], tree[i].max[0]);
swap(tree[i].lmax[1], tree[i].lmax[0]);
swap(tree[i].rmax[1], tree[i].rmax[0]);
return ;
}
int mid = (l + r) >> 1;
if(mid < L) changef(rson, mid + 1, r, L, R);
else if(mid >= R) changef(lson, l, mid, L, R);
else changef(lson, l, mid, L, mid), changef(rson, mid + 1, r, mid + 1, R);
push_up(i);
}
int get_sum(int i, int l, int r, int L, int R){//區間和
push_down(i);
if(L <= l && r <= R){
return tree[i].sum;
}
int mid = (l + r) >> 1, ans = 0;
if(mid < L) return get_sum(rson, mid + 1, r, L, R);
else if(mid >= R) return get_sum(lson, l, mid, L, R);
else return get_sum(lson, l, mid, L, mid) + get_sum(rson, mid + 1, r, mid + 1, R);
}
Tree get_max(int i, int l, int r, int L, int R){//區間最大值
// cout<<i<<" "<<l<<" "<<r<<" "<<L<<" "<<R<<endl;
push_down(i);
if(L <= l && r <= R){ return tree[i]; }
int mid = (l + r)>> 1;
if(mid < L) return get_max(rson, mid + 1, r, L, R);
else if(mid >= R) return get_max(lson, l, mid, L, R);
else {
Tree ans, ansl = get_max(lson, l, mid, L, mid), ansr = get_max(rson, mid + 1, r, mid + 1, R);
ans.sum = ansl.sum + ansr.sum;
ans.lmax[1] = ansl.lmax[1], ans.lmax[0] = ansl.lmax[0];
if(ansl.sum == ansl.len) ans.lmax[1] += ansr.lmax[1];
if(ansl.sum == 0) ans.lmax[0] += ansr.lmax[0];
ans.rmax[1] = ansr.rmax[1], ans.rmax[0] = ansr.rmax[0];
if(ansr.sum == ansr.len) ans.rmax[1] += ansl.rmax[1];
if(ansr.sum == 0) ans.rmax[0] += ansl.rmax[0];
ans.max[1] = max(ansl.max[1], ansr.max[1]);
ans.max[1] = max(ans.max[1], ansl.rmax[1] + ansr.lmax[1]);
ans.max[0] = max(ansl.max[0], ansr.max[0]);
ans.max[0] = max(ans.max[0], ansl.rmax[0] + ansr.lmax[0]);
return ans;
}
}
int main()
{
n = read(), m = read();
for(int i = 1; i <= n; ++i) a[i] = read();
build(1, 1, n);
for(int i = 1, opt, l, r; i <= m; ++i){
opt = read(), l = read() + 1, r = read() + 1;
if(opt == 0){ change01(1, 1, n, l, r, 0); }
if(opt == 1){ change01(1, 1, n, l, r, 1); }
if(opt == 2){ changef(1, 1, n, l, r); }
if(opt == 3){ printf("%d\n", get_sum(1, 1, n, l, r)); }
if(opt == 4){ printf("%d\n", get_max(1, 1, n, l, r).max[1]); }
}
return 0;
}