题意:给你一个n*n(n<=1000)的矩阵,要求求出所有子矩阵的OR和 以及 所有子矩阵的AND和
题解:子矩阵的AND和 可以将矩阵的每一个元素拆成30位二进制 对于每一位二进制讨论贡献 那么每存在一个子矩阵全是1 那么就会对答案贡献乘以这一位的权值 那么这里就设计到一个小技巧 -- 找一个矩阵全是1的子矩阵个数 不会这个知识点便立马学习了一下 受到博客 https://blog.youkuaiyun.com/qq_42814118/article/details/81349964 的启发, 可以枚举每一列 然后枚举一行 从这一行作为矩阵的最大宽度 然后上下延伸(保持单向性防止重复) 延伸的长度乘以宽度便是这个位置的所有矩阵的贡献 同时要事先预处理出来每一行连续1的个数 OR的和便是可以用总的个数减去全是0的子矩阵的个数然后乘以每一位的贡献即可(卡常,注意取模的个数)
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=2e5+7;
typedef long long ll;
const int mod=1e9+7;
#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
ll a[1005][1005],num[1005][1005];
int n,up[1005],down[1005],st[1005];
ll calc(int v){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
num[i][j] = ((a[i][j]&1)==v)?num[i][j-1]+1:0;
}
}
ll sum = 0;
int top = 0;
for(int j=1;j<=n;j++){
top = 0;
for(int i=1;i<=n;i++){
if(num[i][j]){
up[i] = 1;
while(top && num[i][j]<num[st[top]][j]) up[i]+=up[st[top--]]; // 单调栈维护 然后合并
st[++top] = i;
}else{
top = 0;
up[i] = 0;
}
}
top = 0;
for(int i=n;i>=1;i--){
if(num[i][j]){
down[i] = 1;
while(top && num[i][j]<=num[st[top]][j]) down[i]+=down[st[top--]]; //延伸时保持单向性 与上面不一致即可
st[++top] = i;
}else{
top = 0;
down[i] = 0;
}
sum += 1LL*up[i]*down[i]*num[i][j]%mod;
if(sum>=mod) sum -= mod;
}
}
return sum;
}
int Sheryang(){
n = read;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j] = read;
}
}
ll ans1 = 0,ans2 = 0;
ll tot = 1LL*n*(n+1)/2*n*(n+1)/2%mod;//总矩阵的个数
for(int i=0,tmp=1;i<31;i++,tmp<<=1){
ans1 += calc(1)*tmp%mod;
if(ans1>=mod) ans1 -= mod;
ans2 += (tot-calc(0)%mod+mod)%mod*tmp%mod;
if(ans2>=mod) ans2 -= mod;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j] >>= 1;
}
}
}
printf("%lld %lld\n",ans1,ans2);
return 0;
}