# 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 << ' ';
}
```
:::