###### very good data structure # rope & pbds::tree ## rope $\text{rope}$ 就是比較~~粗~~厲害的 $\text{string}$,內部是一棵可持久化平衡樹 大部分的操作都是 $\mathcal{O}(\log n)$ 但常數偏大(我在[這](https://cses.fi/problemset/task/2072)[兩](https://cses.fi/problemset/task/2073)題吃爆 $\text{TLE}$ :crying_cat_face:) ```cpp= #include <bits/stdc++.h> #include <ext/rope> //<bits/extc++.h> using namespace __gnu_cxx; using namespace std; int main() { rope<int> r, a, b; b += 10; // {10} r += 2; // {2} r += 3; // {2, 3} a = r + r + b; // {2, 3, 2, 3, 10} cout << r[0] << endl; // {2} r.pop_back(); // {3} r.pop_front(); // empty r.push_back(1); // {1} r.push_front(10); // {10, 1} r.erase(0, 1); // {1} r.insert(7, 0); // worst case O(n) // {7, 1} cout << r.at(0) << endl; // 輸出 7 r.at(0) = 100; // {100, 1} r[0] = 50; // {50, 1} } ``` 能做的操作和 $\text{string}$ 差不多。 ```cpp= rope<char> r; crope r; // 這兩行是一樣的 ``` [**完整函式庫**](https://www.boost.org/sgi/stl/Rope.html) 什麼時候用的到? 好像用的時機不多 通常是只有刪除和 $\text{append}$ 的時候才會用 其實也不一定要用 $\text{rope}$,但有時候用了會好寫很多 ### 題目們 [**CSES - Josephus Problem II**](https://cses.fi/problemset/task/2163) [**CSES - List Removals**](https://cses.fi/problemset/task/1749) [**TCIRC - b082: Insert and erase**](https://judge.tcirc.tw/ShowProblem?problemid=b082) [**NEOJ - 一天遊戲只能一小時**](https://neoj.sprout.tw/problem/25/) [**NOI 2003 - 文本編輯器**](https://www.luogu.com.cn/problem/P4008) ### 持久化 rope 前面提到 $\text{rope}$ 是一棵可持久化平衡樹 那持久化要怎麼弄? ```cpp= rope<int> *r[100000]; r[i] = new rope<int>(*r[i - 1]); ``` #### 題目 ![alt image](https://i.imgur.com/ZMekauz.png) 感謝優質題目 這題一看就是某種持久化的資結 再仔細看 $1,\ 2,\ 4$ 操作 $\text{rope}$ 都能處理 $3$ 再加上持久化就可以處理了 好耶 :::spoiler AC code (155ms) ```cpp= #include <bits/stdc++.h> #include <bits/extc++.h> using namespace std; using namespace __gnu_cxx; #define LL int_fast64_t #define pLL pair<long long, long long> #define F first #define S second #define pb push_back #define rz resize #define all(x) x.begin(), x.end() #define CH cout << "I'm here" << endl; #define DEBU #pragma GCC optimize("Ofast") #pragma GCC target("avx2") #ifndef DEBUG #define endl '\n' #endif const LL inv2 = 500000004; const LL MOD = 1000000007; const LL INF = 1e18; void Star_Brust_Stream() { ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); } void sol() { LL n, m, q; cin >> n >> m >> q; vector<rope<LL> *> r(n); for (int i = 0; i < n; i++) { r[i] = new rope<LL>; } while (q--) { LL t; cin >> t; if (t == 1) { LL a, b; cin >> a >> b; --a; r[a]->pb(b); } if (t == 2) { LL a; cin >> a; --a; r[a]->pop_back(); } if (t == 3) { LL a, b; cin >> a >> b; --a; --b; r[a] = new rope<LL>(*r[b]); } if (t == 4) { LL a, b; cin >> a >> b; --a; --b; cout << r[a]->at(b) << endl; } } } int main() { Star_Brust_Stream(); LL t = 1; // cin >> t; while (t--) { sol(); } #ifdef DEBUG system("pause"); #endif return 0; } ``` ::: ### 題目 [**E - Notebook**](https://atcoder.jp/contests/abc273/tasks/abc273_e) ## pbds::tree 其實就拿來當 $\text{set/multiset}$ 用 ```cpp= #include <ext/pb_ds/assoc_container.hpp> #include <ext/pb_ds/tree_policy.hpp> /* #include <bits/extc++.h> */ using namespace __gnu_pbds; using namespace std; int main() { tree<int, null_type, less<int>, rb_tree_tag, \ tree_order_statistics_node_update> tr; int a = 1; int k; cin >> k; tr.insert(a); tr.erase(a); tr.lower_bound(a); tr.find_by_order(k); // an iterator of the (k+1)th smallest element tr.order_of_key(k); // the number of elements are less than k } ``` 操作的時間複雜度為 $O(\log n)$ 如果想要當 $\text{multiset}$ 來用的話要把 $\text{less}$ 改成 $\text{less_equal}$ 但 $\text{lower_bound}$ 會變成 $\text{upper_bound}$ $\text{erase}$ 操作只能用 $\text{iterator (I don't know why)}$ ### 逆序數對 **「CDQ? BIT? why not pbds::tree?」 --- 魯迅** ```cpp= for (int i = 0; i < n; i++) { ans += tr.size() - tr.order_of_key(v[i] + 1); tr.insert(v[i]); } ``` ### 更多題目 [**CSES - Josephus Problem II**](https://cses.fi/problemset/task/2163) (again) [**CSES - Nested Ranges Count**](https://cses.fi/problemset/task/2169) [**CSES - Salary Queries**](https://cses.fi/problemset/task/2169) [**CSES - Sliding Median**](https://cses.fi/problemset/task/1076) [**TIOJ - A.逆序數對**](https://tioj.ck.tp.edu.tw/problems/1080)