[GX/GZOI2019]与或和(单调栈+按位运算)
首先看到与或,很显然想到按照位拆分运算。然后就变成了0/1矩阵,要使矩阵在当前位与为1,则矩阵全为1,如果是或为1,则是矩阵不全为0,然后求全为0/1的矩阵个数即可。记录c[i][j]表示以a[i][j]在该位向上0/1的长度。然后对于每一行,单调栈求解即可。
#include<bits/stdc++.h> using namespace std; const int N=1003,mod=1e9+7; int n,ans1,ans2,top,a[N][N],b[N][N],c[N][N],st[N],sum[N]; int calc() { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) c[i][j]=b[i][j]?c[i-1][j]+1:0; int ret=0; for(int i=1;i<=n;i++) { st[0]=top=0; for(int j=1;j<=n;j++) if(!c[i][j])st[0]=j,top=0; else{ while(top&&c[i][j]<=c[i][st[top]])top--; st[++top]=j,sum[top]=(sum[top-1]+1ll*(j-st[top-1])*c[i][j])%mod; ret=(ret+sum[top])%mod; } } return ret; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]); for(int t=0;t<=30;t++) { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) b[i][j]=(a[i][j]>>t)&1; ans1=(ans1+(1ll<<t)*calc())%mod; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) b[i][j]^=1; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) ans2=(ans2+1ll*(n-i+1)*(n-j+1)%mod*(1ll<<t))%mod; ans2=(ans2-(1ll<<t)*calc()%mod+mod)%mod; } printf("%d %d",ans1,ans2); }