20230922 校隊培訓簡報
高翊恩
什麼是資料結構?
...
while (q--) {
cin >> l >> r;
int sum = 0;
for (int i = l; i <= r; i++) {
sum += a[i];
}
cout << sum << endl;
}
...
...
while (q--) {
cin >> l >> r;
int sum = 0;
for (int i = l; i <= r; i++) {
sum += a[i];
}
cout << sum << endl;
}
...
...
while (q--) {
cin >> l >> r;
int sum = 0;
for (int i = l; i <= r; i++) {
sum += a[i];
}
cout << sum << endl;
}
...
\(\Omicron(nq)\)
構造一個新陣列\(p[0..n]\),使得
\(p[0]=0\)
\(p[1]=a[1]\)
\(p[2]=a[1]+a[2]\)
\(p[3]=a[1]+a[2]+a[3]\)
...
\(p[k]=a[1]+a[2]+\dots+a[k-1]+a[k]\)
...
\(p[n]=a[1]+a[2]+a[3]+\dots+a[n-1]+a[n]\)
\(p[0]=0\)
\(p[1]=a[1]\)
\(p[2]=a[1]+a[2]\)
\(p[3]=a[1]+a[2]+a[3]\)
...
\(p[k]=a[1]+a[2]+\dots+a[k-1]+a[k]\)
...
\(p[n]=a[1]+a[2]+a[3]+\dots+a[n-1]+a[n]\)
這樣可以做什麼?
\(p[0]=0\)
\(p[1]=a[1]\)
\(p[2]=a[1]+a[2]\)
\(p[3]=a[1]+a[2]+a[3]\)
...
\(p[k]=a[1]+a[2]+\dots+a[k-1]+a[k]\)
...
\(p[n]=a[1]+a[2]+a[3]+\dots+a[n-1]+a[n]\)
這樣可以做什麼?
可以發現對於一筆詢問\((l,r)\)的解就是
\(p[r]-p[l-1]\)!
...
p[0] = 0;
for (int i = 1; i <= n; i++) {
p[i] = p[i - 1] + a[i];
}
...
...
p[0] = 0;
for (int i = 1; i <= n; i++) {
p[i] = p[i - 1] + a[i];
}
...
\(\Omicron(n)\)
...
p[0] = 0;
for (int i = 1; i <= n; i++) {
p[i] = p[i - 1] + a[i];
}
...
最終複雜度:\(\Omicron(n+q)\)
\(a\)
\(p\)
\(a\)
\(p\)
STL
vector
array
set
stack
deque
queue
multiset
priority_queue
map
pair<int, int> p;
pair<int, int>(int x, int y) | 初始化p | O(1) |
int first; | pair的第一項 | O(1) |
int second; | pair的第二項 | O(1) |
pair<type_A, type_B> make_pair(type_A a, type_B b);
...
#define fs first
#define sc second
#define mp make_pair
typedef pair<int, int> pii;
void DIJKSTRA(int id) {
priority_queue<pii, vector<pii>,greater<pii>> pq;
fill(dis, dis + n, INF);
visited.reset();
pq.push(mp(0, id));
while (pq.size()) {
pii now = pq.top();
pq.pop();
if (visited[now.sc]) continue;
dis[now.sc] = now.fs;
visited[now.sc] = true;
for (auto &i : edge[now.sc]) {
if (visited[i.sc]) continue;
pq.push(mp(now.fs + i.fs, i.sc));
}
}
}
...
queue<int> q;
unsigned size() | 回傳q目前裝了幾個人 | O(1) |
void push(int x) | 將x放入q的最末端 | O(1) |
void pop() | 將q的最前端元素移除 | O(1) |
int &front() | 存取最前端元素 | O(1) |
stack<int> st;
unsigned size() | 回傳st目前裝了幾個人 | O(1) |
void push(int x) | 將x放入st的最末端 | O(1) |
void pop() | 將st的最末端元素移除 | O(1) |
int &top() | 存取最後一個被放入的元素 | O(1) |
vector<int> v;
vector<int>(int n, int x) | 開一個大小為n,每項初始值為x的陣列 | O(n) |
unsigned size() | 回傳v目前裝了幾個人 | O(1) |
void push_back(int x) | 將x放入v的最末端 | O(1) |
void pop_back() | 將v的最末端元素移除 | O(n) |
void clear() | 清除v的所有元素 | O(n) |
int &operator[](int id) | 存取從頭數來的第id項 | O(1) |
array<int, 10> arr;
看起來比較酷的陣列?
deque<int> dq;
unsigned size() | 回傳dq目前裝了幾個人 | O(1) |
void push_front(int x) | 將x放入dq的最前端 | O(1) |
void pop_front() | 將dq的最前端元素移除 | O(1) |
void push_back(int x) | 將x放入dq的最末端 | O(1) |
void pop_back() | 將dq的最末端元素移除 | O(1) |
int &front() | 存取dq最前端元素 | O(1) |
int &back() | 存取dq最末端元素 | O(1) |
void clear() | 清除所有元素 | O(n) |
int &operator[](int id) | 存取從頭數來的第id項 | O(1) |
bitset<1000> b;
void reset() | 將b的每個元素變成false | O(n) |
bool &operator[](int id) | 存取b的第id個bit | O(1) |
priority_queue<int> pq;
unsigned size() | 回傳pq目前裝了幾個人 | O(log n) |
void push(int x) | 將x放入pq | O(log n) |
void pop() | 將pq的最頂端元素移除 | O(log n) |
int &top() | 存取目前最大的元素 | O(1) |
void clear() | 清除pq的所有元素 | O(n) |
set<int> S;
unsigned size() | 回傳目前裝了幾個人 | O(1) |
pair<iterator,bool> insert(int x) | 將x放入S中 | O(log n) |
iterator find(int x) | 回傳指向x的iterator | O(log n) |
void erase(iterator it) | 將it指向的元素移除 | O(log n) |
void clear() | 清除S的所有元素 | O(n) |
multiset<int> MS;
unsigned size() | 回傳目前裝了幾個人 | O(1) |
iterator insert(int x) | 將x放入MS中 | O(log n) |
iterator find(int x) | 回傳指向x的iterator | O(log n) |
void erase(iterator it) | 將it指向的元素移除 | O(log n) |
void clear() | 清除MS的所有元素 | O(n) |
map<int, int> M;
unsigned size() | 回傳目前裝了幾組人 | O(1) |
int &opreator[](int key) | 存取key對應到的值 | O(log n) |
iterator find(int key) | 回傳指向key的iterator | O(log n) |
void erase(iterator it) | 將it指向的元素移除 | O(log n) |
void clear() | 清除M的所有元素 | O(n) |
int32_t main() {
int a[10];
for (int *p = a; p < a + 10; p++) {
cin >> *p;
}
}
int32_t main() {
vector<int> v(10);
for (auto it = v.begin(); it != v.end(); it++) {
cin >> *it;
}
}
隨機存取iterator:
雙向存取iterator:
沒有iterator:
(bitset支援隨機存取,但沒有iterator)
iterator data_str.begin() | 回傳該資料結構的「開頭」iterator |
iterator data_str.end() | 回傳該資料結構的「結尾」iterator |
iterator next(iterator it) | 回傳「下一個」iterator |
iterator prev(iterator it) | 回傳「上一個」iterator |
*it | 存取it指到的位址的那個人 |
it -> func(); it -> attr; | (*it).func(); (*it).attr; |
it + 3 | 回傳「下3個」iterator |
it - 3 | 回傳「上3個」iterator |
it_1 - it_0 | 回傳兩iterator間的距離 |
※vector排序
int32_t main() {
int n;
cin >> n;
int vector<int> v(n);
for (auto &i : v) cin >> i;
sort(v.begin(), v.end());
...
}
※set遍歷
int32_t main() {
int n, br;
set<int> S;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> br;
S.insert(br);
}
for (auto it = S.begin(); it != S.end(); it++) {
cout << *it << ' ';
}
cout << endl;
...
}
※set找人
int32_t main() {
int n, br;
set<int> S;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> br;
S.insert(br);
}
cin >> br;
auto it = S.find(br);
if (it == S.end()) {
cout << "YES" << endl;
} else {
cout << "NO" << endl;
}
...
}
※map遍歷
int32_t main() {
int n, key, val;
map<int, int> M;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> key >> val;
M[key] += val;
}
for (auto it = M.begin(); it != M.end(); it++) {
cout << it -> first >> ": " >> it -> second << endl;
}
}
※map找人
map<int, int> M;
inline int advenced_find(int key) {
auto it = M.find(key);
if (it == M.end()) return -1;
return it -> second;
}
Restaurant Customers
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|
「人數只有在『有人進入』或『有人離開』時才會發生改變。」
為什麼事情變得這麼複雜
※請設計一個資料結構使得可以在\(\Omicron(1)\)的時間內回答此種詢問
2 | 0 | 0 | 8 | 3 | 9 | 0 | 3 | 7 | 1 |
\(\max[2,9)\\=\max(\max[2,5),\max[5,9))\)
2 | 0 | 0 | 8 | 3 | 9 | 0 | 3 | 7 | 1 |
\(\max[2,9)\\=\max(\max[2,5),\max[5,9))\)
2 | 0 | 0 | 8 | 3 | 9 | 0 | 3 | 7 | 1 |
稍微取長一點沒差吧?
\(\max[2,9)\\=\max(\max[2,7),\max[4,9))\)
2 | 0 | 0 | 8 | 3 | 9 | 0 | 3 | 7 | 1 |
2 | 0 | 0 | 8 | 3 | 9 | 0 | 3 | 7 | 1 |
2 | 0 | 0 | 8 | 3 | 9 | 0 | 3 | 7 | 1 |
\(p_4[0..6]:\)
\([8,8,9,9,9,9,7]\)
想求\(\max[3,8)\)
\((n=5)\)
2 | 0 | 0 | 8 | 3 | 9 | 0 | 3 | 7 | 1 |
\(p_4[0..6]:\)
\([8,8,9,9,9,9,7]\)
想求\(\max[3,8)\)
\((n=5)\)
2 | 0 | 0 | 8 | 3 | 9 | 0 | 3 | 7 | 1 |
\(p_4[0..6]:\)
\([8,8,9,9,9,9,7]\)
想求\(\max[3,8)\)
\((n=5)\)
2 | 0 | 0 | 8 | 3 | 9 | 0 | 3 | 7 | 1 |
\(p_4[0..6]:\)
\([8,8,9,9,9,9,7]\)
想求\(\max[3,8)\)
\((n=5)\)
2 | 0 | 0 | 8 | 3 | 9 | 0 | 3 | 7 | 1 |
\(p_4[0..6]:\)
\([8,8,9,9,9,9,7]\)
2 | 0 | 0 | 8 | 3 | 9 | 0 | 3 | 7 | 1 |
\(p_4[0..6]:\)
\([8,8,9,9,9,9,7]\)
想求\(\max[3,9)\)
\((n=6)\)
2 | 0 | 0 | 8 | 3 | 9 | 0 | 3 | 7 | 1 |
\(p_4[0..6]:\)
\([8,8,9,9,9,9,7]\)
想求\(\max[3,9)\)
\((n=6)\)
2 | 0 | 0 | 8 | 3 | 9 | 0 | 3 | 7 | 1 |
\(p_4[0..6]:\)
\([8,8,9,9,9,9,7]\)
想求\(\max[3,9)\)
\((n=6)\)
2 | 0 | 0 | 8 | 3 | 9 | 0 | 3 | 7 | 1 |
\(p_4[0..6]:\)
\([8,8,9,9,9,9,7]\)
想求\(\max[3,9)\)
\((n=6)\)
\(p_1:n\in[1,2]\)
\(p_2:n\in[2,4]\)
\(p_4:n\in[4,8]\)
\(p_8:n\in[8,16]\)
\(\vdots\)
\(p_1[\dots]\)
\(p_2[\dots]\)
\(p_4[\dots]\)
\(p_8[\dots]\)
\(\vdots\)
struct SPT {
int n, p[__lg(MXN)][MXN];
void init(int _n, int *a) {
n = _n;
for (int i = 0; i < n; i++) p[0][i] = a[i];
for (int i = 1, k = 2; i <= __lg(n); i++, k *= 2) {
for (int j1 = 0, j2 = k / 2, j1 <= n - k; j1++, j2++) {
p[i][j1] = max(p[i - 1][j1], p[i - 1][j2]);
}
}
}
int query(int l, int r) {
// [l, r)
int n = r - l;
int k_lg = __lg(n);
return max(p[k_lg][l], p[k_lg][r - (1 << k_lg)]);
}
};
※請設計一個資料結構使得可以支援改值操作,並利用某種方法維護前綴和
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
b[1]: |
b[2]: |
b[3]: |
b[4]: |
b[5]: |
b[6]: |
b[7]: |
b[8]: |
(1-indexed)
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
b[1]:2 |
b[2]:2 |
b[3]:0 |
b[4]:7 |
b[5]:1 |
b[6]:1 |
b[7]:1 |
b[8]:17 |
(1-indexed)
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
b[1]:2 |
b[2]:2 |
b[3]:0 |
b[4]:7 |
b[5]:1 |
b[6]:1 |
b[7]:1 |
b[8]:17 |
(1-indexed)
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
b[1]:2 |
b[2]:2 |
b[3]:0 |
b[4]:7 |
b[5]:1 |
b[6]:1 |
b[7]:1 |
b[8]:17 |
(1-indexed)
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
b[1]:2 |
b[2]:2 |
b[3]:0 |
b[4]:7 |
b[5]:1 |
b[6]:1 |
b[7]:1 |
b[8]:17 |
(1-indexed)
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
b[1]:2 |
b[2]:2 |
b[3]:0 |
b[4]:7 |
b[5]:1 |
b[6]:1 |
b[7]:1 |
b[8]:17 |
(1-indexed)
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
b[1]:2 |
b[2]:2 |
b[3]:0 |
b[4]:7 |
b[5]:1 |
b[6]:1 |
b[7]:1 |
b[8]:17 |
(1-indexed)
+2
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
b[1]:2 |
b[2]:2 |
b[3]:0 |
b[4]:7 |
b[5]:1 |
b[6]:1 |
b[7]:1 |
b[8]:17 |
(1-indexed)
+2
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
b[1]:2 |
b[2]:2 |
b[3]:0 |
b[4]:7 |
b[5]:1 |
b[6]:1 |
b[7]:1 |
b[8]:17 |
(1-indexed)
+2
+2
+2
+2
\(6_{10}=110_2\ \Rightarrow\ \operatorname{lowbit}(6)=2\)
在接到操作時,如何遍歷過所有需要存取的\(id\)?
for (; id <= n; id += (id & -id)) {
}
for (; id > 0; id -= (id & -id)) {
}
struct BIT {
int n, val[MXN];
void init(int _n) {
n = _n;
for (int i = 1; i <= n; i++) val[i] = 0;
}
void modify(int id, int x) {
for (; id <= n; id += (id & -id)) val[id] += x;
}
int query(int id) {
// [1, id]
int ans = 0;
for (; id > 0; id -= (id & -id)) ans += val[id];
return ans;
}
};
※請設計一個資料結構使得可以支援改值操作,並利用某種方法維護區間最大值
「如果做得到\(n=2^a\)的詢問,會讓查詢複雜度降低」
「維護住『小部分』的區間,會讓改值複雜度降低」
Sparse Table:
BIT:
struct SMG_NODE {
int l, r, val;
SMG_NODE *L, *R;
};
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
5
1
0
1
8
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
5
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
5
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
5
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
5
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
5
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
5
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
5
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
5
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
5
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
5
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
5
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
5
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
3
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
3
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
3
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
5
1
8
2
0
0
3
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
3
1
8
2
0
0
3
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
3
1
8
2
0
0
3
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
3
1
8
2
0
0
3
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
5
8
2
3
1
8
2
0
0
3
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
/ 3
2 | 0 | 0 | 5 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
/ 3
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
\(\Rightarrow\)兩個子節點都修改
// in struct:
void modify(int _pos, int _val) {
if (!(l <= _pos && _pos < r)) return;
if (L == nullptr) val = _val;
else {
L -> modify(_pos, _val);
R -> modify(_pos, _val);
val = max(L -> val, R -> val);
}
}
「查詢區間」與該節點「維護區間」的關係必為下列其中一種:
「查詢區間」與該節點「維護區間」的關係必為下列其中一種:
\(\Rightarrow\) return -INF;
\(\Rightarrow\) return val;
\(\Rightarrow\) 遞迴後比較兩子節點回傳值
// in struct:
int query(int _l, int _r) {
if (_r <= l || r <= _l) return -INF;
if (_l <= l && r <= _r) return val;
return max(L -> query(_l, _r), R -> query(_l, _r));
}
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
2 | 0 | 0 | 3 | 1 | 0 | 1 | 8 |
8
3
8
2
3
1
8
2
0
0
3
1
0
1
8
struct SMG_NODE {
int l, r, val;
SMG_NODE *L, *R;
SMG_NODE(int _l, int _r, int _val) : l(_l), r(_r), val(_val), L(nullptr), R(nullptr) {}
void modify(int _pos, int _val) {
if (!(l <= _pos && _pos < r)) return;
if (L == nullptr) val = _val;
else {
L -> modify(_pos, _val);
R -> modify(_pos, _val);
val = max(L -> val, R -> val);
}
}
int query(int _l, int _r) {
if (_r <= l || r <= _l) return -INF;
if (_l <= l && r <= _r) return val;
return max(L -> query(_l, _r), R -> query(_l, _r));
}
};
SMG_NODE *build(int _l, int _r, int *a) {
if (_l + 1 == _r) return new SMG_NODE(_l, _r, a[_l]);
SMG_NODE *ans = new SMG_NODE(_l, _r, -INF);
int mid = (_l + _r) >> 1;
ans -> L = build(_l, mid, a);
ans -> R = build(mid, _r, a);
ans -> val = max(L -> val, R -> val);
return ans;
}
struct SMG {
int val[MXN * 4];
void modify(int id, int l, int r, int _pos, int _val) {
if (!(l <= id && id < r)) return;
if (l + 1 == r) {
val[id] = _val;
return;
}
int mid = (l + r) >> 1;
modify(id * 2 + 1, l, mid, _pos, _val);
modify(id * 2 + 2, mid, r, _pos, _val);
val[id] = max(val[id * 2 + 1], val[id * 2 + 2]);
}
int query(int id, int l, int r, int _l, int _r) {
if (_r <= l || r <= _l) return -INF;
if (_l <= l && r <= _r) return val[id];
int mid = (l + r) >> 1;
return max(query(id * 2 + 1, l, mid, _l, _r), query(id * 2 + 2, mid, r, _l, _r));
}
};