# Gojo - Editorial # MiniMaxi - Editorial Đầu tiên, ta có thể nhìn bài toán theo hướng khác, thay vì tính tất cả $f(l,r)$ theo $max(a_i) - min(a_i) \forall (l \le i \le r)$, ta sẽ tính tổng các $max(a_i) \forall (l \le i \le r)$ trước, rồi trừ đi các $min(a_i) \forall (l \le i \le r)$. Tính $max$: - Gọi $L_i$ là vị trí nhỏ nhất mà $a_i > a_j \forall (L_i \le j \le i-1)$. Gọi $R_i$ là vị trí lớn nhất mà $a_i \ge a_j \forall (i+1 \le j \le R_i)$. Dãy $L_i$ và $R_i$ có thể dùng $stack$ để tính trong $O(N)$, nếu chưa biết bạn có thể tham khảo tại [đây](https://vnoi.info/wiki/algo/data-structures/Stack.md). - Như vậy với mỗi vị trí $i$, giá trị $a_i$ sẽ được cộng vào kết quả $(i-L_i+1)*(R_i-i+1)$ lần. Cứ làm như vậy với mọi i, ta sẽ tính được tổng của các $max$. - Để hiểu tại sao ở đoạn $L_i$ ta lấy $a_i > a_j$ mà không lấy $a_i \ge a_j$ như ở đoạn tính $R_i$, bạn có thể tham khảo test sau: + 3 3 1 3 Làm tương tự khi tính $min$, nhưng lần này, thay vì cộng vào kết quả, ta trừ nó vào kết quả. Độ phức tạp: $O(N)$ Code: ```cpp= #include<bits/stdc++.h> using namespace std; #define int long long const int N=5e5+5; int n,m; int a[N],b[N]; int res=0; int l[N], r[N]; void minn(int c[]){ stack<int> st; for (int i=1; i<=m; i++) { while (!st.empty()&&c[i]<c[st.top()]) st.pop(); l[i] = st.empty() ? 1 : st.top()+1; st.push(i); } stack<int> s; for (int i=m; i>=1; i--){ while (!s.empty()&&c[i]<=c[s.top()]) s.pop(); r[i] = s.empty() ? m : s.top()-1; s.push(i); } for (int i=1; i<=m; i++) { // cout << l[i] << ' ' << r[i] << '\n'; res-=a[i]*(i-l[i]+1)*(r[i]-i+1); } } void maxx(int c[]){ stack<int> st; for (int i=1; i<=m; i++) { while (!st.empty()&&c[i]>c[st.top()]) st.pop(); l[i] = st.empty() ? 1 : st.top()+1; st.push(i); } stack<int> s; for (int i=m; i>=1; i--){ while (!s.empty()&&c[i]>=c[s.top()]) s.pop(); r[i] = s.empty() ? m : s.top()-1; s.push(i); } for (int i=1; i<=m; i++) { // cout << l[i] << ' ' << r[i]<< '\n'; res+=a[i]*(i-l[i]+1)*(r[i]-i+1); } } signed main(){ ios_base::sync_with_stdio(false); cin.tie(0); cin>>m; for (int i=1; i<=m; i++) cin>>a[i]; minn(a); for (int i=1; i<=m; i++) l[i]=r[i]=0; maxx(a); cout << res; } ``` # NRect - Editorial Nếu ta có thể tính được hàm $cal(x)$ là số hình chữ nhật có $min \ge x$, thì kết quả của bài toán sẽ là $cal(100) - cal(101)$. Để tính hàm $cal(x)$, ta có thể quy về một bài toán mới, gọi ma trận $b$ gồm $n$ hàng và $n$ cột, $b_{ij} = 0$ nếu $a_{ij} < x$, ngược lại $b_{ij} = 1$. Bài toán lúc này chuyển thành đếm số hình chữ nhật trong ma trận $b$ gồm toàn số $1$. Để làm được điều này, ta làm như sau: - Cố định từng hàng, có thể coi đây là *đáy* của hình chữ nhật ta đang xét. - Sau đó, giả sử ta đang cố định hàng $i$, gọi $h_j$ là số số $1$ liên tiếp trên cột $j$ bắt đầu từ đáy là hàng $i$: - Gọi $l_j$ là vị trí xa nhất về bên trái mà $h_j$ là $min$ trên đoạn $[l_j,j]$, $r_j$ là vị trí xa nhất về bên phải mà $h_j$ là $min$ **và không có số nào bằng với h_j** trên đoạn $[j,r_j]$. - Dễ thấy, kết quả lúc này sẽ cộng thêm $(r[j] - j + 1) * (j - l[j] + 1)*h[i][j]$. - Việc $r_j$ thêm điều kiện **không có số nào bằng với h_j** sẽ đảm bảo ta không tính trùng hình chữ nhật. - Hai mảng $l$, $r$ có thể dễ dàng tính bằng $stack$. Độ phức tạp: $O(n^2)$. Code: ```cpp #include<bits/stdc++.h> using namespace std; #define int long long int n; const int N = 1005; int a[N][N]; int c[N][N]; int h[N][N]; int l[N],r[N]; int solve (int val) { int res = 0; for (int i=1; i<=n; i++) { for (int j=1; j<=n; j++) { c[i][j] = a[i][j] >= val; // cout << c[i][j] << ' '; } // cout << '\n'; } // cout << "??\n"; for (int i=1; i<=n; i++) { for (int j=1; j<=n; j++) { if (c[i][j]) { h[i][j] = h[i-1][j] + 1; } else h[i][j] = 0; } } for (int i=1; i<=n; i++) { stack<int> st; st.push(0); for (int j=1; j<=n; j++) { while (st.size() >= 2 && h[i][st.top()] >= h[i][j]) st.pop(); l[j] = st.top() + 1; st.push(j); } while (st.size()) st.pop(); st.push(n+1); for (int j=n; j>=1; j--) { while (st.size() >= 2 && h[i][st.top()] > h[i][j]) st.pop(); r[j] = st.top() - 1; st.push(j); res += (r[j] - j + 1) * (j - l[j] + 1)*h[i][j]; // cout << i << ' ' << j << ' ' << l[j] << ' ' << r[j] << '\n'; } } // cout << res << "?\n"; return res; } signed main() { ios_base::sync_with_stdio(false); cin.tie(0); cin >> n; for (int i=1; i<=n; i++) { for (int j=1; j<=n; j++) cin >> a[i][j]; } cout << solve (100) - solve(101); } ```