[COCI2010-2011#6] STEP
阿新 • • 發佈:2020-08-09
題目大意
維護一個 \(01\) 序列最長的連續相鄰兩個數不同的子序列的長度
解析
很裸的線段樹題。。。
要維護的資訊很多
- 區間長度
- 區間最左端點
- 區間最右端點
- 區間最長字首
- 區間最長字尾
- 區間最終的答案
前三個直接從左右兒子獲取即可
區間最長字首先為左兒子的區間最長字首
如果左兒子的區間最長字首為左區間的長度,那麼考慮它能不能和右兒子拼接,更新答案
右區間最長字首同理
最終答案是 左兒子的最終答案,右兒子的最終答案,左兒子和右兒子能否拼接的答案,本區間的最長字首,本區間的最長字尾 中的最大值
於是你明白了為什麼要維護辣麼多東西
\(Code\)
#include<cstdio> #include<iostream> #define ls (k << 1) #define rs (ls | 1) using namespace std; const int N = 2e5 + 5; int n , q; struct segment{ int len , l , r , p , s , v; }seg[N << 2]; inline void pushup(int k) { seg[k].l = seg[ls].l , seg[k].r = seg[rs].r; seg[k].len = seg[ls].len + seg[rs].len; seg[k].p = seg[ls].p; if (seg[ls].p == seg[ls].len && seg[ls].r ^ seg[rs].l) seg[k].p = seg[ls].p + seg[rs].p; seg[k].s = seg[rs].s; if (seg[rs].s == seg[rs].len && seg[ls].r ^ seg[rs].l) seg[k].s = seg[rs].s + seg[ls].s; seg[k].v = (max(seg[k].p , seg[k].s) , max(seg[ls].v , seg[rs].v)); if (seg[ls].r ^ seg[rs].l) seg[k].v = max(seg[k].v , seg[ls].s + seg[rs].p); } inline void build(int l , int r , int k) { if (l == r) { seg[k].l = seg[k].r = 0; seg[k].len = seg[k].p = seg[k].s = seg[k].v = 1; return; } int mid = (l + r) >> 1; build(l , mid , ls) , build(mid + 1 , r , rs); pushup(k); } inline void update(int x , int l , int r , int k) { if (l == r && l == x) { seg[k].l = seg[k].r = seg[k].l ^ 1; return; } int mid = (l + r) >> 1; if (x <= mid) update(x , l , mid , ls); else update(x , mid + 1 , r , rs); pushup(k); } int main() { scanf("%d%d" , &n , &q); build(1 , n , 1); int x; for(; q; q--) { scanf("%d" , &x); update(x , 1 , n , 1); printf("%d\n" , seg[1].v); } }