很顯然是拆位
那麼對於and,就是統計這個01矩陣中的全1子矩陣個數
對於or,就是統計這個01矩陣中的全0子矩陣個數,再用全集減去它
統計01矩陣的全1子矩陣個數就直接上單調棧即可
大致做法:
記錄矩陣內每一個點上方的連續1有多少個(包括自身)
掃描每一行,求出以每個點爲右下角的矩陣的個數,可以用單調棧彈掉不合法的答案
Code:
#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
inline int add(int x,int y){x+=y;if(x>=mod) x-=mod;return x;}
inline int dec(int x,int y){x-=y;if(x<0) x+=mod;return x;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline void inc(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void Dec(int &x,int y){x-=y;if(x<0) x+=mod;}
inline void Mul(int &x,int y){x=1ll*x*y%mod;}
inline int ksm(int a,int b){int res=1;for(;b;b>>=1,a=mul(a,a)) if(b&1) res=mul(res,a);return res;}
const int N=1e3+5;
int a[N][N],s[N][N],sta[N],top;
int n,ans;
inline void calc(){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) s[i][j]=a[i][j]*(s[i-1][j]+1);
for(int i=1;i<=n;i++){
sta[top=0]=0;int res=0;
for(int j=1;j<=n;j++){
inc(res,s[i][j]);
while(top && s[i][sta[top]]>=s[i][j])
Dec(res,mul(sta[top]-sta[top-1],s[i][sta[top]]-s[i][j])),--top;
inc(ans,res);sta[++top]=j;
}
}
}
int tmp[N][N],ansand=0,ansor=0;
int main(){
n=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) tmp[i][j]=read();
for(int d=0;d<=30;d++){
int res=(1<<d)%mod;
ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) a[i][j]=((tmp[i][j]>>d)&1);
calc();
inc(ansand,mul(res,ans));
ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) a[i][j]^=1;
calc();
int tmpp=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) inc(tmpp,i*j);
inc(ansor,mul(res,dec(tmpp,ans)));
}
cout<<ansand<<" "<<ansor;
return 0;
}