# Gợi ý segtri 22/08/2023 ## [Cuộc đua](https://marisaoj.com/problem/363) - Mảng $P$ đáp án phải có tính chất số lượng $P_j > P_i$ mà $j < i$ là $A_i$. - Hãy thứ khôi phục mảng $P$ bằng cách tìm lần lượt xem, các thí sinh $i$ từ $1$ đến $n$ về đích thứ bao nhiêu. - Tối ưu từ $n^2 \log n$ về $n \log n$ bằng kĩ thuật Segment Tree Walk. Code :::spoiler ```c++ #include <bits/stdc++.h> using namespace std; #define pi pair<int, int> #define inf32 0x3f3f3f3f #define inf64 0x3f3f3f3f3f3f3f3f #define all(x) x.begin(), x.end() #define Unique(v) v.erase(unique(all(v)), v.end()); #define int long long #define setinf(d) memset(d, inf32, sizeof d) #define setneg(d) memset(d, -1, sizeof d) #define set0(d) memset(d, 0, sizeof d) #define Log2(x) 63 - __builtin_clzll(x) #define oo 2e18 #define mod 1000000007 #define FILENAME "f" const int maxn = 1e6 + 5; int n; int a[maxn]; int ans[maxn]; int lmao[maxn]; struct SegmentTree{ vector<int> node; SegmentTree(int n): node(4 * n + 2){} void build(int v, int l, int r){ if(l == r){ node[v] = 1; return; } int m = (l + r) >> 1; build(v * 2, l, m); build(v * 2 + 1, m + 1, r); node[v] = node[v * 2] + node[v * 2 + 1]; } void update(int v, int l, int r, int pos, int val){ if(r < pos || l > pos) return; if(l == r && pos == r){ node[v] += val; return; } int m = (l + r) >> 1; update(v * 2, l, m, pos, val); update(v * 2 + 1, m + 1, r, pos, val); node[v] = node[v * 2] + node[v * 2 + 1]; } int search(int v, int l, int r, int val){ if(l == r) return l ; int m = (l + r) >> 1; if(node[v * 2] >= val) return search(v * 2, l, m, val); else return search(v * 2 + 1, m + 1, r, val - node[v * 2]); } }; signed main(){ ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n; for(int i = 1; i <= n; i++) cin >> a[i]; SegmentTree t(n); t.build(1, 1, n); for(int i = 1; i <= n; i++){ int p = t.search(1, 1, n, a[i] + 1); ans[p] = i; t.update(1, 1, n, p, -1); } for(int i = 1; i <= n; i++) cout << ans[i] << ' '; } ``` ::: ## [XOR mảng con](https://marisaoj.com/problem/362) - Hãy thử xử lí với trường hợp $A_i,x = 0/1$ - Ví dụ với mảng $\{0, 1, 1, 0, 0, 1\}$, hãy tìm cách tính tổng XOR của toàn bộ các đoạn con. Cách tính: :::spoiler Với $P$ là mảng tiền tố XOR: $\{0, 0, 1, 0, 0, 0, 1\}$ (thêm một số $0$ ở đầu). Đáp án chính là số lượng số $0$ nhân với số lượng số $1$ = $10$. ::: - Khi biết cách làm với giá trị $0/1$, hãy tạo ra $\log n$ cây Segment Tree để, xử lí mỗi bit riêng biệt. **Các bài liên quan đến XOR có rất nhiều khả năng xử lí các bit riêng biệt.** Code :::spoiler ```c++ #include <bits/stdc++.h> using namespace std; #define pi pair<int, int> #define inf32 0x3f3f3f3f #define inf64 0x3f3f3f3f3f3f3f3f #define all(x) x.begin(), x.end() #define Unique(v) v.erase(unique(all(v)), v.end()); #define setinf(d) memset(d, inf32, sizeof d) #define setneg(d) memset(d, -1, sizeof d) #define set0(d) memset(d, 0, sizeof d) #define Log2(x) 63 - __builtin_clzll(x) #define oo 2e18 #define mod 1000000007 #define FILENAME "f" const int maxn = 1e5 + 5; int n, q; int a[maxn]; int pre[maxn][31]; struct Node{ int odd, even, lazy; Node operator + (const Node &a){ return {a.odd + odd, a.even + even}; } }; struct SegmentTree{ vector<Node> node; SegmentTree(int n): node(n * 8 + 1){} void push(int v){ if(node[v].lazy){ swap(node[v * 2].even, node[v * 2].odd); swap(node[v * 2 + 1].even, node[v * 2 + 1].odd); node[v * 2].lazy ^= 1; node[v * 2 + 1].lazy ^= 1; node[v].lazy = 0; } } void build(int v, int l, int r, int pos, int val){ if(r < pos || l > pos) return; if(pos == l && l == r){ node[v].even = (val % 2 == 0); node[v].odd = val % 2; return; } int m = (l + r) >> 1; build(v * 2, l, m, pos, val); build(v * 2 + 1, m + 1, r, pos, val); node[v] = node[v * 2] + node[v * 2 + 1]; } void update(int v, int l, int r, int tl, int tr){ if(r < tl || l > tr) return; if(tl <= l && r <= tr){ swap(node[v].odd, node[v].even); node[v].lazy ^= 1; return; } push(v); int m = (l + r) >> 1; update(v * 2, l, m, tl, tr); update(v * 2 + 1, m + 1, r, tl, tr); node[v] = node[v * 2] + node[v * 2 + 1]; } Node get(int v, int l, int r, int tl, int tr){ if(r < tl || l > tr) return Node{0, 0}; else if(tl <= l && r <= tr) return node[v]; push(v); int m = (l + r) >> 1; return get(v * 2, l, m, tl, tr) + get(v * 2 + 1, m + 1, r, tl, tr); } }; vector<SegmentTree> d(31, SegmentTree(maxn)); void build(int x, int idx){ int pos = 0; for(int i = 0; i < 31; i++) pre[idx][i] = pre[idx - 1][i]; while(x){ // cout << (x & 1) << ' ' << x << ' ' << idx << endl; pre[idx][pos] += x & 1; // cout << pos << ' ' << idx << ' ' << pre[idx][pos] << endl; x >>= 1; pos++; } } void modify(int x, int i){ // cout << "mod " << x << endl; int pos = 0; while(x){ Node t = d[pos].get(1, 0, n, i, i); bool old = t.odd; // cout << pos << ' '<< old << ' ' << (x & 1) << endl; if((x & 1)) d[pos].update(1, 0, n, i, n); x >>= 1; pos++; } } signed main(){ ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> q; for(int i = 1; i <= n; i++) cin >> a[i], build(a[i], i); for(int i = 0; i < 31; i++) for(int j = 0; j <= n; j++) d[i].build(1, 0, n, j, pre[j][i]);//, cout << j << ' ' << i << ' ' << pre[j][i] << endl; while(q--){ int t, a, b; cin >> t >> a >> b; if(t == 1){ modify(b ^ ::a[a], a); ::a[a] = b; }else{ // for(int i = 1; i <= n; i++){ // cout << ::a[i] << ' '; // } // cout << endl; Node ans; long long int qans = 0; for(int i = 0; i < 31; i++){ ans = d[i].get(1, 0, n, a - 1, b); // cout << i << ' '<< ans.odd << ' ' << ans.even << endl; qans += (1 << i) * (long long)ans.odd * (long long)ans.even; } cout << qans << '\n'; } } } ``` ::: ## [Radio](https://marisaoj.com/problem/365) - $dp_i$ là thời gian nhỏ nhất để truyền tín hiệu đến radar $i$. - Có một cây segtri, với vị trí $v$ là $dp_v$. - Để tính được $dp_i$, ta có thể query min trên segtri từ $l_1$ đến $r_1$, nhưng như thế có thể query nhầm vào các radar không thể truyền đến $i$. Nghĩa là radar đó có $l_2$, $r_2$ mà $i < l_2$ hoặc $r_2 < i$. - Ta xử lí bằng cách, khi tính đến $dp_i$, các radar $v$ có $l_2$ bằng $i$ thì ta cập nhật $dp_v$ vào segtri, khi tính đến $dp_i$ mà $v$ có $r_2+1$ bằng $i$ thì xóa khỏi segtri. Khi đó luôn đảm bảo các radar trong segtri có $l_2 \le i \le r_2$. Code :::spoiler ```c++ #include <bits/stdc++.h> using namespace std; #define pi pair<int, int> #define inf32 0x3f3f3f3f #define inf64 0x3f3f3f3f3f3f3f3f #define all(x) x.begin(), x.end() #define Unique(v) v.erase(unique(all(v)), v.end()); #define int long long #define setinf(d) memset(d, inf32, sizeof d) #define setneg(d) memset(d, -1, sizeof d) #define set0(d) memset(d, 0, sizeof d) #define Log2(x) 63 - __builtin_clzll(x) #define oo 2e18 #define mod 1000000007 #define FILENAME "f" const int maxn = 1e5 + 5; int n, dp[maxn], val[maxn]; vector<int> add[maxn], del[maxn]; struct SegmentTree{ vector<int> node; SegmentTree(int n): node(4 * n + 69){ build(1, 1, n); } void build(int v, int l, int r){ node[v] = inf64; if(l == r){ return; } int m = (l + r) >> 1; build(v << 1, l, m); build(v << 1 | 1, m + 1, r); } void update(int v, int l, int r, int pos, int val){ if(r < pos || l > pos) return; if(l == r && pos == l){ node[v] = val; return; } int m = (l + r) >> 1; update(v * 2, l, m, pos, val); update(v * 2 + 1, m + 1, r, pos, val); node[v] = min(node[v * 2], node[v * 2 + 1]); } int get(int v, int l, int r, int tl, int tr){ if(r < tl || l > tr) return inf64; if(tl <= l && r <= tr) return node[v]; int m = (l + r) >> 1; return min(get(v * 2, l, m, tl, tr), get(v * 2 + 1, m + 1, r, tl, tr)); } }; signed main(){ ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n; SegmentTree d(n); setinf(dp); dp[1] = 0; for(int i = 1; i <= n; i++){ int t, l1, r1, l2, r2; cin >> t >> l1 >> r1 >> l2 >> r2; val[i] = t; assert(t); assert(l1); assert(r1); assert(l2); assert(r2); add[max(i + 1, l2)].push_back(i); del[r2 + 1].push_back(i); for(int &v : add[i]){ // cout << "add " << v << ' ' << i << ' ' << dp[v] - v + val[t] << endl; d.update(1, 1, n, v, dp[v] - v + val[v]); } for(int &v : del[i]){ d.update(1, 1, n, v, inf64); } if(i != 1){ dp[i] = d.get(1, 1, n, l1, r1) + t + i; } ////d.update(1, 1, n, i, dp[i] - i + t); } for(int i = 2; i <= n; i++) if(dp[i] < inf64 / 2) cout << dp[i] << ' '; else cout << -1 << ' '; } ``` :::