1. 程式人生 > 實用技巧 >P2572 [SCOI2010]序列操作

P2572 [SCOI2010]序列操作

寫在前面

傻逼線段樹題,碼量第一次超 \(6.5k\)

因為不能考 \(NOIp\) 一氣之下把它肝了(放屁,你是因為想看小說,而做資料結構不用腦子,好在DJ來的時候方便掩飾

個人感覺理解本題後會對線段樹有更為深刻的理解,亦可增加對線段樹的套路用法,瞭解各操作之間的優先順序

本題解陳述儘量詳細周全,若有贅述的地方請自行跳過,如果有什麼疑問也可在評論區提出

簡述題意

題面

給定一段 \(01\) 序列,有 \(5\) 種操作

0、一段區間變成 \(0\)
1、一段區間變成 \(1\)
2、對一段區間取反
3、詢問一段區間內有多少個 \(1\)
4、詢問一段區間內最多有多少個連續的 \(1\)

序列長為 \(n\)

,有 \(m\) 個操作(資料範圍:\(1 \le n,m\le 10^5\)

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;
}