Appearance
名字来自于 CF896C 的题图。
本质是用数据结构维护区间连续段。
出于实际使用情况考虑,省略链表维护的介绍。
以下实现中,mp[x] 表示从位置 mp 的第一个键是左端点
init
点击展开代码
cpp
void init(int l, int r) {
assert(l <= r && r < INT_MAX);
mp.clear();
mp[l] = mp[r + 1] = - 1;
}split
点击展开代码
cpp
auto split(int x) {
assert(!mp.empty());
assert(mp.begin()->first <= x && x <= mp.rbegin()->first);
auto it = mp.lower_bound(x);
if (it != mp.end() && it->first == x) {
return it;
}
return mp.emplace_hint(it, x, prev(it)->second);
}assign
区间推平,一般是区间赋值。
点击展开代码
cpp
void assign(int l, int r, int v) {
assert(!mp.empty());
assert(mp.begin()->first <= l && l <= r && r < mp.rbegin()->first);
int sentinel = mp.rbegin()->first;
auto itr = split(r + 1);
auto itl = split(l);
mp.erase(itl, itr);
auto it = mp.emplace_hint(itr, l, v);
if (it != mp.begin() && prev(it)->second == it->second) {
auto pre = prev(it);
mp.erase(it);
it = pre;
}
auto nxt = next(it);
if (nxt != mp.end() && nxt->first != sentinel && nxt->second == it->second) {
mp.erase(nxt);
}
}perform
遍历区间
点击展开代码
cpp
void perform(int l, int r, int v) {
assert(!mp.empty());
assert(mp.begin()->first <= l && l <= r && r < mp.rbegin()->first);
auto itr = split(r + 1);
auto it = split(l);
while (it != itr) {
// 具体操作
it = next(it);
}
}时间复杂度分析
区间推平时
若每次操作后,都会进行一次区间推平操作,即将区间合并成一个相同的连续段。
每个区间只会被遍历一次,每次最多增加一个区间,最多增加到
数据随机时
CF896C 证明了这种情况的时间复杂度为
