【题目】
给定一个
n
×
n
n\times n
n×n的矩阵,求其所有子矩阵的与和的和以及所有子矩阵的或和的和。其中子矩阵的与和为其中所有数的与值,或和同理。答案对
1
0
9
+
7
10^9+7
109+7取模。
n
≤
1000
,
a
i
≤
2
31
−
1
n\leq 1000,a_i\leq 2^{31}-1
n≤1000,ai≤231−1
【解题思路】
首先肯定是对每一位分开算。对于与和,相当于求有多少个全
1
1
1子矩阵。对于或和,相当于用总数减去全
0
0
0子矩阵个数。
那么这个问题是一个单调栈经典问题。首先对于每个位置处理出它往右最多能扩展多少,枚举左边界后,就是求所有子区间的最小值之和。接下来就再用单调栈处理出每个值为最小值的区间就可以了。注意单调栈是半闭半开的。
复杂度 O ( n 2 log a ) O(n^2\log a) O(n2loga)
【参考代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1005,mod=1e9+7;
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
namespace DreamLolita
{
int n,ans[2];
int a[N][N],b[N][N],R[N][N];
int U[N],D[N],now[N],st[N];
int calc()//calc all 1
{
int res=0,top;
memset(R,0,sizeof(R));
for(int i=1;i<=n;++i) for(int j=n;j;--j) R[i][j]=(b[i][j]?R[i][j+1]+1:0);
//puts("now:");
//for(int i=1;i<=n;++i,puts("")) for(int j=1;j<=n;++j) printf("%d ",R[i][j]);
//puts("end");
for(int l=1;l<=n;++l)
{
memset(U,0,sizeof(U));memset(D,0,sizeof(D));
now[0]=now[n+1]=-1;for(int i=1;i<=n;++i) now[i]=R[i][l];
//for(int i=1;i<=n;++i) printf("%d ",now[i]); puts("");
top=0;st[++top]=0;
for(int i=1;i<=n;++i)
{
while(top && now[st[top]]>=now[i]) --top;
U[i]=st[top]+1;st[++top]=i;
}
top=0;st[++top]=n+1;
for(int i=n;i;--i)
{
while(top && now[st[top]]>now[i]) --top;
D[i]=st[top]-1;st[++top]=i;
}
//for(int i=1;i<=n;++i) printf("%d ",U[i]); puts("");
//for(int i=1;i<=n;++i) printf("%d ",D[i]); puts("");
for(int i=1;i<=n;++i) res=(res+1ll*(D[i]-i+1)*(i-U[i]+1)*now[i]%mod)%mod;
}
return res;
}
void solution()
{
n=read();
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) a[i][j]=read();
int all=(1+n)*n/2;all=1ll*all*all%mod;
for(int op=0;op<2;++op)
{
for(int k=0;k<31;++k)
{
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) b[i][j]=(a[i][j]>>k&1)^op;
//printf("%d %d %d\n",op,k,calc());
if(!op) ans[op]=(ans[op]+1ll*calc()*(1<<k)%mod)%mod;
else ans[op]=(ans[op]+1ll*(all-calc()+mod)%mod*(1<<k)%mod)%mod;
}
}
printf("%d %d\n",ans[0],ans[1]);
}
}
int main()
{
#ifdef Durant_Lee
freopen("LOJ3083.in","r",stdin);
freopen("LOJ3083.out","w",stdout);
#endif
DreamLolita::solution();
return 0;
}