---
# System prepended metadata

title: NRect - Editorial

---

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