P2572 SCOI2010 序列操作

2022-05-05 19:48:18 字數 2988 閱讀 8806

對自己 & \(rng\) : 驕兵必敗

\(lpl\)加油!

題目描述

lxhgww最近收到了乙個01序列,序列裡面包含了n個數,這些數要麼是0,要麼是1,現在對於這個序列有五種變換操作和詢問操作:

0 a b 把[a, b]區間內的所有數全變成0

1 a b 把[a, b]區間內的所有數全變成1

2 a b 把[a,b]區間內的所有數全部取反,也就是說把所有的0變成1,把所有的1變成0

3 a b 詢問[a, b]區間內總共有多少個1

4 a b 詢問[a, b]區間內最多有多少個連續的1

對於每一種詢問操作,lxhgww都需要給出回答,聰明的程式設計師們,你們能幫助他嗎?

錯誤日誌: 內容較多且重要, 將寫在下方加粗

線段樹, 需要維護以下資訊:

\(sum\) 區間 \(1\) 的個數

\(max[0/1]\) 區間內 \(0/1\) 最長連續子段

\(lmax[0/1]\) 包含區間左端點最長 \(0/1\) 子段

\(rmax[0/1]\) 包含區間右端點最長 \(0/1\) 子段

鑑於有區間操作, 這裡還需要兩個標記:

5. \(lazy = \) 為 \(-1\) 表示無狀態, 為 \(0/1\) 表示區間全部賦值為 \(0/1\)

6. \(rev = \) 區間是否翻轉

需要維護的元素較多, 先講 \(pushup\)

區間和直接相加即可

包含左端點的連續子段有兩種情況:

直接繼承左區間的 \(lmax\)

當左區間全滿 / 全空時, 左端點可以跨越, 加上右區間的部分

右區間更新同理

對於區間最長連續子段, 有以下三種情況

直接繼承左區間的較大值

直接繼承右區間的較大值

左區間的含右端點最長子段 + 右區間含左端點最長子段, 即最長部分跨越區間分割線

以上需要分 \(0/1\) 討論, 程式中直接兩層迴圈搞定

然後到了難點 \(pushdown\)

我乙個需要衝省隊的選手竟然直到現在才注意到這點很慘啊操作中, 我們需要明確兩件事:

標記的優先順序

2. 下放某一標記是否會對子節點的其他型別標記有所影響

這裡重點討論第二點(第一點區間全體賦值優先順序肯定高於翻轉, 所以優先拆區間賦值標記, 拆標記時需要將翻轉標記清空)

在拆解乙個標記時, 我們不僅需要明確將此標記下放到子節點,同型別的標記應該如何改變, 而更應明確拆解此標記會對不同型別的標記有何種影響

明確同型別的影響是一般不會出問題的, 如將區間加標記下放時, 子節點的區間加標記累加上這個值

以本題為例:

將區間賦值標記拆解時, 不僅需要將子區間賦值標記更新為此值, 還需要將子節點翻轉標記清空(不過這個貌似影響不大, 拆賦值標記時會將翻轉標記清空的)

將區間翻轉標記拆解時, 需要分兩種情況考慮此標記下推對子區間 賦值標記 和 翻轉標記造成的影響

考慮到賦值標記的優先順序大於翻轉標記,在有賦值標記的情況下, 直接翻轉賦值標記

其餘情況翻轉標記異或等於1

其餘見** 雖然很長但用心弄懂會對線段樹有乙個很深刻的理解

這樣完備地考慮所有情況是乙個 \(oi\) 選手的基本素養

嗯就這樣, \(rng\) 別喪氣, 你們是最棒的; \(s8\) ,\(lpl\) 加油!

#include#include#include#include#include#include#define ll long long

#define rep(i, x, y) for(int i = (x);i <= (y);i++)

using namespace std;

int rd()

while(c >= '0' && c <= '9')

return flag * out;

}const int maxn = 200019;

int num, na;

int v[maxn];

#define lid (id << 1)

#define rid (id << 1) | 1

//int max(int a, int b)//聽說手寫快?

struct seg_treetree[maxn << 2];

void pushup(int id)

}void build(int id, int l, int r)

int mid = (l + r) >> 1;

build(lid, l, mid), build(rid, mid + 1, r);

pushup(id);

}void pushdown(int id)

if(tree[id].rev)

}void update(int id, int val, int l, int r)

else if(val == 2)

return ;

}int mid = (tree[id].l + tree[id].r) >> 1;

if(mid < l)update(rid, val, l, r);

else if(mid >= r)update(lid, val, l, r);

else update(lid, val, l, mid), update(rid, val, mid + 1, r);

pushup(id);

}int query(int id, int l, int r)

seg_tree q_max(int id, int l, int r)

return ret;}}

int main()

return 0;

}

P2572 SCOI2010 序列操作

include include define n 100005 using namespace std int n,m struct seg t n 2 共13個成員 void rev int k 第k個點取反 在外層修改了取反標誌 void color int k,int v 第k個點全改成0 1...

P2572 SCOI2010 序列操作

這道題給你好多的01串,還有好多的區間統一賦值。沒錯,你想到了什麼?珂朵莉樹!所以你就可以用珂朵莉樹很輕鬆地水過這道題了!唯一要注意的是split的順序。必須先split右邊的,再split左邊的。原因是先split左邊的時候,可能會因為split右邊而導致原迭代器被刪掉了,所以左邊的迭代器會是乙個...

P2572 SCOI2010 序列操作

線段樹神仙操作 珂朵莉樹基本操作?珂朵莉樹是不可能的,這輩子只會碼線段樹,只有線段樹神仙操作才刺激,debug之後ac才最快樂 這題我折騰了半個下午加半個晚上。維護的東西太多了。如果沒有區間反轉,這題很簡單,但是有反轉,所以既要維護1,又要維護0。tot記錄區間中1的個數 len記錄區間長度 l記錄...