又是GXOI2019里水水的送分题,然而我看错题+细节写挂愣是搞了一个多小时(深深的明白自己的弱小)
首先看到二进制我们就考虑到枚举每一位,算子矩阵中这一位为(1)的方案数再乘起来吧
然后这时候矩阵就变成了(0/1)矩阵(其实样例1就在提示你这种做法),我们考虑(operatorname{AND})时的答案,即求出全(1)矩阵个数
这个很套路吧,我们预处理出(h_{i,j})表示((i,j))以上(包括((i,j))最多有多少个连续的(1))
接下来考虑求出以每一个点为矩阵的右下角时的方案数,每一列的情况可以单调栈处理:
考虑连续递增的一段的答案(如((3,5,6))我们是可以计算的,就是一个求和的过程),那么我们就维护单调递增的单调栈
然后考虑每次加入一个数,那么前面大于它的都会被他删掉,此时的贡献其实就是删掉数的个数( imes)当前的(h_{i,j})
因此单调栈里再维护一下前缀和以及数的个数(因为被删除的要累积到删除它的数上),就完成了(operatorname{AND})的做法
那么(operatorname{OR})呢,其实直接补集转化一下就好了,用总方案数减去全(0)矩阵方案数即为所求
因此综上,复杂度(O(n^2log a_i)),足以通过此题
CODE
#include<cstdio>
#include<cctype>
#define RI register int
#define CI const int&
#define Tp template <typename T>
using namespace std;
const int N=1005,mod=1e9+7;
int n,a[N][N],and_ans,or_ans,mx,h[N][N],st[N],sum[N],val[N],top,cur;
class FileInputOutput
{
private:
static const int S=1<<21;
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
char Fin[S],*A,*B;
public:
Tp inline void read(T& x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
#undef tc
}F;
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
inline int add(CI x,CI y)
{
int t=x+y; return t>=mod?t-mod:t;
}
inline int solve_and(CI bs,int ret=0)
{
RI i,j; for (i=1;i<=n;++i) for (j=1;j<=n;++j)
if (a[i][j]&bs) h[i][j]=h[i-1][j]+1; else h[i][j]=0;
for (i=1;i<=n;++i) for (top=0,j=1;j<=n;++j) if (!h[i][j]) top=0; else
{
int high=1; while (top&&st[top]>h[i][j]) high+=val[top--];
inc(ret,1LL*h[i][j]*high%mod); inc(ret,sum[top]); st[++top]=h[i][j];
val[top]=high; sum[top]=add(sum[top-1],1LL*h[i][j]*val[top]%mod);
}
return 1LL*ret*bs%mod;
}
inline int solve_or(CI bs,int ret=0)
{
RI i,j; for (i=1;i<=n;++i) for (j=1;j<=n;++j)
if (!(a[i][j]&bs)) h[i][j]=h[i-1][j]+1; else h[i][j]=0;
for (i=1;i<=n;++i) for (top=0,j=1;j<=n;++j) if (!h[i][j]) top=0; else
{
int high=1; while (top&&st[top]>h[i][j]) high+=val[top--];
inc(ret,1LL*h[i][j]*high%mod); inc(ret,sum[top]); st[++top]=h[i][j];
val[top]=high; sum[top]=add(sum[top-1],1LL*h[i][j]*val[top]%mod);
}
ret=cur-ret; if (ret<0) ret+=mod; return 1LL*ret*bs%mod;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i,j; for (F.read(n),i=1;i<=n;++i) for (j=1;j<=n;++j)
F.read(a[i][j]),mx=a[i][j]>mx?a[i][j]:mx,inc(cur,1LL*i*j%mod);
for (i=1;i<=mx&&i>0;i<<=1) inc(and_ans,solve_and(i)),inc(or_ans,solve_or(i));
return printf("%d %d",and_ans,or_ans),0;
}