满足对于所有
l
≤
i
≤
r
l\le i\le r
l≤i≤r 都有
a
l
−
1
>
a
i
,
a
r
+
1
>
a
i
a_{l-1}>a_i,a_{r+1}>a_i
al−1>ai,ar+1>ai 的二元组
(
l
,
r
)
(l,r)
(l,r) 只有
O
(
n
)
O(n)
O(n) 个
证明可以使用单调栈
把这个性质放回原问题
相当于如果某一行强制被选出,那么合法的列区间只有
O
(
m
)
O(m)
O(m) 个
同样地,如果某一列强制被选出,那么合法的行区间也只有
O
(
n
)
O(n)
O(n) 个
取
m
i
d
=
⌊
1
+
n
2
⌋
mid=\lfloor\frac{1+n}2\rfloor
mid=⌊21+n⌋ ,考虑统计过第
m
i
d
mid
mid 行的子矩形数量,即第
m
i
d
mid
mid 行被强制选出
先利用单调栈枚举出所有合法的列区间
[
l
,
r
]
[l,r]
[l,r]
现在要做的就是统计有多少个合法子矩形,行区间包含了
m
i
d
mid
mid 且列区间为
[
l
,
r
]
[l,r]
[l,r]
列区间为
[
l
,
r
]
[l,r]
[l,r] 相当于第
l
l
l 列到第
r
r
r 列都被强制选出
O
(
n
m
(
log
n
+
log
m
)
)
O(nm(\log n+\log m))
O(nm(logn+logm))
如果有
O
(
n
m
)
O(nm)
O(nm) 做法欢迎提出
Code
#include<bits/stdc++.h>template<classT>inlinevoidread(T &res){
res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')
res =(res <<3)+(res <<1)+(c -48);if(bo) res =~res +1;}template<classT>inline T Min(const T &a,const T &b){return a < b ? a : b;}template<classT>inline T Max(const T &a,const T &b){return a > b ? a : b;}constint N =2505, M = N <<1, LogN =12;int n, m, a[N][N], top, stk[N], tot, L[M], R[M], le[N][N], ri[N][N],
up[N][N][LogN], dn[N][N][LogN], Log[N], ans;intupmax(int i,int l,int r){int k = Log[r - l +1];returnMax(up[i][l][k], up[i][r -(1<< k)+1][k]);}intdnmin(int i,int l,int r){int k = Log[r - l +1];returnMin(dn[i][l][k], dn[i][r -(1<< k)+1][k]);}boolavail(int xl,int xr,int yl,int yr){returndnmin(xl -1, yl, yr)> xr &&upmax(xr +1, yl, yr)< xl;}voidsolve(int l,int r){if(l > r)return;int mid = l + r >>1;
top = tot =0;for(int i =1; i <= m; i++){while(top && a[mid][i]> a[mid][stk[top]]){if(stk[top]+1< i) L[++tot]= stk[top]+1, R[tot]= i -1;
top--;}if(top && stk[top]+1< i) L[++tot]= stk[top]+1, R[tot]= i -1;
stk[++top]= i;}for(int i =1; i <= tot; i++){
top =0;int x = L[i], il = mid, ir = mid;while(il > l -1&& ri[il][L[i]-1]> R[i]&& le[il][R[i]+1]< L[i]) il--;while(ir < r +1&& ri[ir][L[i]-1]> R[i]&& le[ir][R[i]+1]< L[i]) ir++;for(int j = il; j <= ir; j++){while(top && a[j][x]> a[stk[top]][x]){if(stk[top]+1<= mid && j -1>= mid
&&avail(stk[top]+1, j -1, L[i], R[i])) ans++;
top--;}if(top && stk[top]+1<= mid && j -1>= mid
&&avail(stk[top]+1, j -1, L[i], R[i])) ans++;
stk[++top]= j;}}solve(l, mid -1);solve(mid +1, r);}intmain(){read(n);read(m);for(int i =1; i <= n; i++)for(int j =1; j <= m; j++)read(a[i][j]);for(int i =1; i <= n; i++){
stk[top =0]=0;for(int j =1; j <= m; j++){while(top && a[i][j]> a[i][stk[top]]) top--;
le[i][j]= stk[top]; stk[++top]= j;}
stk[top =0]= m +1;for(int j = m; j >=1; j--){while(top && a[i][j]> a[i][stk[top]]) top--;
ri[i][j]= stk[top]; stk[++top]= j;}}for(int j =1; j <= m; j++){
stk[top =0]=0;for(int i =1; i <= n; i++){while(top && a[i][j]> a[stk[top]][j]) top--;
up[i][j][0]= stk[top]; stk[++top]= i;}
stk[top =0]= n +1;for(int i = n; i >=1; i--){while(top && a[i][j]> a[stk[top]][j]) top--;
dn[i][j][0]= stk[top]; stk[++top]= i;}}
Log[0]=-1;for(int i =1; i <= m; i++) Log[i]= Log[i >>1]+1;for(int i =1; i <= n; i++)for(int k =1; k <=11; k++)for(int j =1; j +(1<< k)-1<= m; j++){
up[i][j][k]=Max(up[i][j][k -1], up[i][j +(1<< k -1)][k -1]);
dn[i][j][k]=Min(dn[i][j][k -1], dn[i][j +(1<< k -1)][k -1]);}solve(2, n -1);return std::cout << ans << std::endl,0;}