//自己想出来的方法 队友说是矩阵dp 那就这样叫吧
//思路: 选一个宽度 和起始列对应的下标 取出对应的区域 求出取出区域内 的子矩阵的最大和 与当前最大和比较
// 当取遍所有子矩阵的时候当前最大和 就是 此矩阵所有子矩阵的 最大和
//eg:1 1 1 1
// 2 2 2 2
// 3 3 3 3
// 1 1 1 1
//如果 l=2 ,b=1 则得到区域
// 1 [1 1] 1
// 2 [2 2] 2
// 3 [3 3] 3
// 1 [1 1] 1
//对应一维数组为2 4 6 2 求出这个一维数组的最大连续子序列的和 就是上图区域内所有n=l m=任意可取值 的子矩阵的最大和
//
//
#include<stdio.h>
#include<string.h>
#define N 200
int map[N][N],sum[N][N],a[N];//map[][]存矩阵 sum[i][j]表示第i行从sum[i][0]+sum[i][1]+…+sum[i][j]的和(预处理 为了下面二维到一维的转化做准备)
int max,num,ma,nu;//max存最大值 num 存最大值的数量 ma存临时最大值 nu存临时最大值的数量
int t,n,m;
void init()
{
scanf("%d%d",&n,&m);
int i,j;
memset(sum,0,sizeof(sum));
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
scanf("%d",&map[i][j]);
if(j!=0)
sum[i][j]=sum[i][j-1]+map[i][j];
else
sum[i][j]=map[i][j];
}
}
max=-9999999;
num=0;
}
void geta(int l,int b)//化二维为一维(把宽度为l 每行开始位置为b(下标)的子矩阵 化为一维数组a[])
{
int i;
for(i=0;i<n;i++)
{
if(b!=0)
a[i]=sum[i][b+l-1]-sum[i][b-1];
else
a[i]=sum[i][b+l-1];
}
}
void findmax()//求当前子矩阵(已化为一维数组)的最大和(一维数组连续子序列和的最大值)
{
int i,j,s,f,q,flag;
ma=-99999999;
nu=1;
s=0;
q=0;
//因为如序列0 0 1 2 3 0 子序列的最大值为 6 最大值个数为6(1 2 3 | 0 1 2 3 | 0 0 1 2 3 | 0 1 2 3 0 | 1 2 3 0 | 0 0 1 2 3 0 )而不是 1
//所以用q 统计“真正最大子序列”(不包含前导和后导0)前导0的个数
for(i=0,j=0;i<n,j<n;)
{
f=1;
for(j=i;j<n;j++)
{
if(a[j]==0&&f)//f=1表示没有遇到过非0的数即当前的a[j]为前导0 则q++
q++;
else
f=0;//遇到非零数 f赋为0
s+=a[j];
if(s>ma)//解决前导0 的问题
{
ma=s;
if(q==0)//如果无前导0 则当前子串为“真正子序列”即nu=1;
nu=1;
else if(ma==0)//若该序列为0 则nu=1;
nu=1;
else// 若该子序列为 0 0 0 1 2 3 则nu=q+1;
nu=q+1;
}
else if(ma==s)//解决后导0 的问题
{
if(ma==0)//若此序列为0 0 0 0(全0)则每增加一个后继0 nu增加的值与当前0 的总个数相等 (而当前0的总个数就是q)
nu+=q;
else//若此序列不是全0 序列 如0 0 0 1 2 3 0 0 则每增加一个后继0 nu增加q+1
nu=nu+q+1;
}
//这样做也解决了 如0 0 1 2 3 -100 0 0 6 0 (即有两个子序列的和都为最大值)的问题
if(s<0)
{
s=0;
i=j+1;
q=0;
f=1;
break;
}
}
}
}
void solve()
{
int l,b;
for(l=1;l<=m;l++)
{
for(b=0;b<=m-l;b++)
{
geta(l,b);//得到从b开始宽度为l的子矩阵对应的一维数组
findmax();//求一维数组连续子序列的最大和的最大值 和个数
if(ma==max)
{
num+=nu;
}
else if(ma>max)
{
num=nu;
max=ma;
}
}
}
printf("%d %d\n",max,num);
}
int main()
{
scanf("%d",&t);
while(t--)
{
init();
solve();
}
return 0;
}
//思路: 选一个宽度 和起始列对应的下标 取出对应的区域 求出取出区域内 的子矩阵的最大和 与当前最大和比较
// 当取遍所有子矩阵的时候当前最大和 就是 此矩阵所有子矩阵的 最大和
//eg:1 1 1 1
// 2 2 2 2
// 3 3 3 3
// 1 1 1 1
//如果 l=2 ,b=1 则得到区域
// 1 [1 1] 1
// 2 [2 2] 2
// 3 [3 3] 3
// 1 [1 1] 1
//对应一维数组为2 4 6 2 求出这个一维数组的最大连续子序列的和 就是上图区域内所有n=l m=任意可取值 的子矩阵的最大和
//
//
#include<stdio.h>
#include<string.h>
#define N 200
int map[N][N],sum[N][N],a[N];//map[][]存矩阵 sum[i][j]表示第i行从sum[i][0]+sum[i][1]+…+sum[i][j]的和(预处理 为了下面二维到一维的转化做准备)
int max,num,ma,nu;//max存最大值 num 存最大值的数量 ma存临时最大值 nu存临时最大值的数量
int t,n,m;
void init()
{
scanf("%d%d",&n,&m);
int i,j;
memset(sum,0,sizeof(sum));
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
scanf("%d",&map[i][j]);
if(j!=0)
sum[i][j]=sum[i][j-1]+map[i][j];
else
sum[i][j]=map[i][j];
}
}
max=-9999999;
num=0;
}
void geta(int l,int b)//化二维为一维(把宽度为l 每行开始位置为b(下标)的子矩阵 化为一维数组a[])
{
int i;
for(i=0;i<n;i++)
{
if(b!=0)
a[i]=sum[i][b+l-1]-sum[i][b-1];
else
a[i]=sum[i][b+l-1];
}
}
void findmax()//求当前子矩阵(已化为一维数组)的最大和(一维数组连续子序列和的最大值)
{
int i,j,s,f,q,flag;
ma=-99999999;
nu=1;
s=0;
q=0;
//因为如序列0 0 1 2 3 0 子序列的最大值为 6 最大值个数为6(1 2 3 | 0 1 2 3 | 0 0 1 2 3 | 0 1 2 3 0 | 1 2 3 0 | 0 0 1 2 3 0 )而不是 1
//所以用q 统计“真正最大子序列”(不包含前导和后导0)前导0的个数
for(i=0,j=0;i<n,j<n;)
{
f=1;
for(j=i;j<n;j++)
{
if(a[j]==0&&f)//f=1表示没有遇到过非0的数即当前的a[j]为前导0 则q++
q++;
else
f=0;//遇到非零数 f赋为0
s+=a[j];
if(s>ma)//解决前导0 的问题
{
ma=s;
if(q==0)//如果无前导0 则当前子串为“真正子序列”即nu=1;
nu=1;
else if(ma==0)//若该序列为0 则nu=1;
nu=1;
else// 若该子序列为 0 0 0 1 2 3 则nu=q+1;
nu=q+1;
}
else if(ma==s)//解决后导0 的问题
{
if(ma==0)//若此序列为0 0 0 0(全0)则每增加一个后继0 nu增加的值与当前0 的总个数相等 (而当前0的总个数就是q)
nu+=q;
else//若此序列不是全0 序列 如0 0 0 1 2 3 0 0 则每增加一个后继0 nu增加q+1
nu=nu+q+1;
}
//这样做也解决了 如0 0 1 2 3 -100 0 0 6 0 (即有两个子序列的和都为最大值)的问题
if(s<0)
{
s=0;
i=j+1;
q=0;
f=1;
break;
}
}
}
}
void solve()
{
int l,b;
for(l=1;l<=m;l++)
{
for(b=0;b<=m-l;b++)
{
geta(l,b);//得到从b开始宽度为l的子矩阵对应的一维数组
findmax();//求一维数组连续子序列的最大和的最大值 和个数
if(ma==max)
{
num+=nu;
}
else if(ma>max)
{
num=nu;
max=ma;
}
}
}
printf("%d %d\n",max,num);
}
int main()
{
scanf("%d",&t);
while(t--)
{
init();
solve();
}
return 0;
}