算法作用
- 区间赋值
- 区间加
- 区间第k大值
- … …
除了区间赋值assign以外,其他的操作都可以简单的称之为“暴力”。可以在模仿 “区间加” 的操作下,暴力实现其他的操作。
时间复杂度
在数据随机的情况下,效率较高,可以达到 n l o g n l o g n nlognlogn nlognlogn
Code(注释版)
struct node
{
ll l, r;
mutable ll v;
//mutable 的意思是“可变的”,意味着可以直接修改已经插入 set 中的元素的 v 值,
//而不用将该元素取出后重新加入 set
node(ll l, ll r, ll v) : l(l), r(r), v(v) {} // 构造函数
bool operator<(const node& o) const { return l < o.l; } // 重载小于运算符
};
set<node> tree;
auto split(ll pos)
{
auto it = tree.lower_bound(node(pos, 0, 0));
//如果已经存在以pos为左端点的节点,直接返回
if (it != tree.end() && it->l == pos) return it;
//否则往前数一个节点,该节点为x所在的区间
it--;
ll l = it->l, r = it->r, v = it->v;
tree.erase(it); // 删除该节点
tree.insert(node(l, pos - 1, v)); // 插入<l,pos-1,v>和<pos,r,v>
return tree.insert(node(pos, r, v)).first; // 返回以pos开头的那个节点的迭代器
// insert默认返回值是一个pair,第一个成员是我们要的
}
//区间赋值
void assign(ll l, ll r, ll v)
{
//在进行求取区间左右端点操作时,必须先 split 右端点,再 split 左端点。
//若先 split 左端点,返回的迭代器可能在 split 右端点的时候失效,可能会导致 RE。
auto end = split(r + 1), begin = split(l);
tree.erase(begin, end); // 清除一系列节点
tree.insert(node(l, r, v)); // 插入新的节点
}
//区间加
void add(ll l, ll r, ll v)
{
auto end = split(r + 1);
for (auto it = split(l); it != end; it++)
it->v += v;
}
Code(简洁版)
struct node
{
ll l, r;
mutable ll v;
node(ll l, ll r, ll v) : l(l), r(r), v(v) {}
bool operator<(const node& o) const { return l < o.l; }
};
set<node> tree;
auto split(ll pos)
{
auto it = tree.lower_bound(node(pos, 0, 0));
if (it != tree.end() && it->l == pos) return it;
it--;
ll l = it->l, r = it->r, v = it->v;
tree.erase(it);
tree.insert(node(l, pos - 1, v));
return tree.insert(node(pos, r, v)).first;
}
//区间赋值
void assign(ll l, ll r, ll v)
{
auto end = split(r + 1), begin = split(l);
tree.erase(begin, end);
tree.insert(node(l, r, v));
}
//区间加
void add(ll l, ll r, ll v)
{
auto end = split(r + 1);
for (auto it = split(l); it != end; it++)
it->v += v;
}