Unverified Commit 153df7d2 authored by Alpha1022's avatar Alpha1022 Committed by GitHub
Browse files

Create odt.md

parent e770f683
Loading
Loading
Loading
Loading

docs/misc/odt.md

0 → 100644
+95 −0
Original line number Diff line number Diff line
# 名称简介

老司机树,ODT(Old Driver Tree),珂朵莉树(Ctholly Tree)。  
起源自[](https://www.luogu.org/problemnew/show/CF896C)

# 前置知识

会用 STL 的 set 就行。

# 核心思想

把值相同的区间合并成一个结点保存在 set 里面。

# 用处

骗分。  
只要是有区间赋值操作的数据结构题都可以用来骗分。  
一般出题人不会**刻意**卡,但是不小心卡了就……

如果要保证复杂度正确,必须保证数据随机。  
证明在[](http://codeforces.com/blog/entry/56135?#comment-398940)

# 正文

首先,结点的保存方式:

```cpp
struct node {
  int l, r;
  mutable int v;
  inline bool operator(const node &o) const { return l < o.l; }
};
```

其中, `int v` 是你自己指定的附加数据。

然后,我们定义一个 `set<node> odt;` 来维护这些结点。  
为简化代码,可以 `typedef set<node>::iterator IT`

## split

最核心的操作之一 `split` ,它用于取得以 $x$ 开头的结点。  
参考代码如下:

```cpp
IT split(int x) {
  if (x > n)
    ;
  return odt.end();
  IT it = --odt.upper_bound((node){x, 0, 0});
  if (it->l == x) return it;
  int l = it->l, r = it->r, v = it->v;
  odt.erase(it);
  odt.insert((node){l, x - 1, v});
  return odt.insert((node){x, r, v}).first;
}
```

这个玩意有什么用呢?  
任何对于 $[l,r]$ 的区间操作,都可以转换成 set 上 $[split(l),split(r + 1))$ 的操作。

## assign

另外一个重要的操作 `assign` 用于对一段区间进行赋值。  
对于 ODT 来说,区间操作只有这个比较特殊,也是保证复杂度的关键。  
如果 ODT 里全是长度为 $1$ 的区间,就成了暴力,但是有了 `assign` ,可以使 ODT 的大小下降。  
参考代码如下:

```cpp
void assign(int l, int r, int v) {
  IT itr = split(r + 1), itl = split(l);
  odt.erase(itl, itr);
  odt.insert((node){l, r, v});
}
```

## 其他操作

套模板就好了,参考代码如下:

```cpp
void performance(int l, int r) {
  IT itr = split(r + 1), itl = split(l);
  for (; itl != itr; ++itl) {
    // Perform here
  }
}
```

# 习题

-   [BZOJ 1858.\[SCOI2010\] 序列操作](https://www.lydsy.com/JudgeOnline/problem.php?id=1858)
-   [BZOJ 4592.\[SHOI2015\] 脑洞治疗仪](https://www.lydsy.com/JudgeOnline/problem.php?id=4592)
-   [洛谷 2787. 理理思维](https://www.luogu.org/problemnew/show/P2787)
-   [洛谷 4979. 矿洞:坍塌](https://www.luogu.org/problemnew/show/P4979)