LG5300 「GZOI2019/GXOI2019」与或和 二进制+单调栈
问题描述
Freda 学习了位运算和矩阵以后,决定对这种简洁而优美的运算,以及蕴含深邃空间的结构进行更加深入的研究。
对于一个由非负整数构成的矩阵,她定义矩阵的 \(\texttt{AND}\) 值为矩阵中所有数二进制 \(\texttt{AND(&)}\) 的运算结果;定义矩阵的 \(\texttt{OR}\) 值为矩阵中所有数二进制 \(\texttt{OR(|)}\) 的运算结果。
给定一个 \(N \times N\) 的矩阵,她希望求出:
- 该矩阵的所有子矩阵的 \(\texttt{AND}\) 值之和(所有子矩阵 \(\texttt{AND}\) 值相加的结果)。
- 该矩阵的所有子矩阵的 \(\texttt{OR}\) 值之和(所有子矩阵 \(\texttt{OR}\) 值相加的结果)。
接下来的剧情你应该已经猜到——Freda 并不想花费时间解决如此简单的问题,所以这个问题就交给你了。
由于答案可能非常的大,你只需要输出答案对 \(1,000,000,007 (10^9 + 7)\) 取模后的结果。
题解
发现 and 和 or 大毒瘤出现开幕雷击,于是考虑按位计算贡献。
问题就简化为了01矩阵。
于是一个子矩阵全是0的话对or有负贡献,全是1的话对and有正贡献。
单调栈维护即可
\(\mathrm{Code}\)
#include<bits/stdc++.h>
using namespace std;
#define int long long
template <typename Tp>
void read(Tp &x){
x=0;char ch=1;int fh=1;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') ch=getchar(),fh=-1;
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
x*=fh;
}
const int mod=1000000007;
const int maxn=1007;
int a[maxn][maxn],n;
long long ansor,ansand;
int s[maxn],h[maxn],val[maxn],pos;
int mx;
void Init(void){
read(n);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)
read(a[i][j]),mx=max(mx,a[i][j]);
}
long long sqr(long long x){
return (long long)x*(x+1)/2;
}
int calc(int need){
long long res=0;
memset(h,0,sizeof(h));
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if((a[i][j]&1)==need) h[j]++;
else h[j]=0;
if(h[j]>s[pos]) s[++pos]=h[j],val[pos]=1;
else{
int k=0;
while(h[j]<s[pos]){
k+=val[pos];
res=(res+(s[pos]-max(s[pos-1],h[j]))*sqr(k)%mod)%mod;
--pos;
}
s[++pos]=h[j],val[pos]=k+1;
}
}
int k=0;
while(pos){
k+=val[pos];
res=(res+(s[pos]-s[pos-1])*sqr(k)%mod)%mod;
--pos;
}
}
return res;
}
void Work(void){
int tp=0;
while(mx){
ansand=(ansand+calc(1)*(1<<tp)%mod)%mod;
ansor=(ansor+(sqr(n)*sqr(n)%mod-calc(0))*(1<<tp)%mod)%mod;
mx>>=1;tp++;
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) a[i][j]>>=1;
}
ansand=(ansand%mod+mod)%mod;
ansor=(ansor%mod+mod)%mod;
printf("%lld %lld\n",ansand,ansor);
}
signed main(){
// freopen("andorsum.in","r",stdin);freopen("andorsum.out","w",stdout);
Init();
Work();
}