對自己 & \(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記錄...