Saddle Point
Time Limit: 1 Second Memory Limit: 131072 KB
Chiaki has an n × m matrix A. Rows are numbered from 1 to n from top to bottom and columns are numbered from 1 to m from left to right. The element in the i-th row and the j-th column is Ai, j.
Let M({i1, i2, …, is}, {j1, j2, …, jt}) be the matrix that results from deleting row i1, i2, …, is and column j1, j2, …, jt of A and f({i1, i2, …, is}, {j1, j2, …, jt}) be the number of saddle points in matrix M({i1, i2, …, is}, {j1, j2, …, jt}).
Chiaki would like to find all the value of f({i1, i2, …, is}, {j1, j2, …, jt}). As the output may be very large ((2n - 1)(2m - 1) matrix in total), she is only interested in the value
Note that a saddle point of a matrix is an element which is both the only largest element in its column and the only smallest element in its row.
Input
There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:
The first line contains four integers n and m (1 ≤ n, m ≤ 1000) – the number of rows and the number of columns.
Each of the next n lines contains m integer Ai, 1, Ai, 2, …, Ai, m (1 ≤ Ai, j ≤ 106), where Ai, j is the integer in the i-th row and the j-th column.
It is guaranteed that neither the sum of all n nor the sum of all m exceeds 5000.
Output
For each test case, output an integer denoting the answer.
Sample Input
2
2 2
1 1
1 1
4 5
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
Sample Output
4
465
主要思路是 考虑A[i][j]在多少个子矩阵中能作为 鞍点
A[i][j]在所在行元素中 必须是最小,列中必须最大
那对原矩阵,如果
A[i][k]>A[i][j]
,那第k列可以选择
同理 如果
A[k][j]<A[i][j]
,那第k行是可以选择的
每行每列有(选/不选)2种情况
以A[i][j]作为鞍点的子矩阵数量=2∑m−1k=0(A[i][k]>A[i][j])+∑n−1k=0(A[k][j]<A[i][j])
原矩阵的所有子矩阵鞍点数量=∑0<=i<n,0<=j<m2∑m−1k=0(A[i][k]>A[i][j])+∑n−1k=0(A[k][j]<A[i][j])
所以只需要通过二分找出
A[i][k]>A[i][j]以及A[k][j]<A[i][j]
的数量进行快速幂再累加即可
O(n∗m∗max(log(n),log(m)))
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<string>
#include<vector>
#include<deque>
#include<queue>
#include<algorithm>
#include<set>
#include<map>
#include<stack>
#include<ctime>
#include<string.h>
#include<math.h>
#include<list>
using namespace std;
#define ll long long
#define pii pair<int,int>
const int inf = 1e9 + 7;
inline int read(){
int x;
char ch;
while(!isdigit(ch=getchar()));
x=ch-'0';
while(isdigit(ch=getchar())){
x=x*10+ch-'0';
}
return x;
}
const int N = 1e3+5;
int mat[N][N];
int r[N][N];//r[i][j]=第i行第j个数
int c[N][N];//c[i][j]=第i列的第i个数
int qPow(int a,int n){
ll ans=1;
ll t=a;
while(n){
if(n&1){
ans=(ans*t)%inf;
}
n>>=1;
t=(t*t)%inf;
}
return ans;
}
int slove(int n,int m){
for(int i=0;i<n;++i){
sort(r[i],r[i]+m);
}
for(int i=0;i<m;++i){
sort(c[i],c[i]+n);
}
int ans=0;
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
int rn=(r[i]+m)-upper_bound(r[i],r[i]+m,mat[i][j]);
int cn=lower_bound(c[j],c[j]+n,mat[i][j])-c[j];
ans=(ans+qPow(2,rn+cn))%inf;
}
}
return ans;
}
int main()
{
//freopen("/home/lu/Documents/r.txt","r",stdin);
//freopen("/home/lu/Documents/w.txt","w",stdout);
int T;
scanf("%d",&T);
while(T--){
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
scanf("%d",&mat[i][j]);
c[j][i]=r[i][j]=mat[i][j];
}
}
printf("%d\n",slove(n,m));
}
return 0;
}