luogu P5300 [GXOI/GZOI2019]与或和

传送门

题目涉及按位与以及按位或运算,所以可以拆位考虑,枚举某个二进制位,然后某个位置如果那个数的第\(i\)位是\(0\)就放\(0\),否则放\(1\),这一位的贡献就是位运算后值为\(1\)的子矩阵个数\(*2^i\).对于与运算,权值为\(1\)的矩阵为全\(1\)矩阵;对于或运算,权值为\(1\)的矩阵为含有\(1\)的矩阵,可以看成是总个数-全\(0\)矩阵个数,然后全\(0\)和全\(1\)矩阵个数可以单调栈求得

#include<bits/stdc++.h>
#define LL long long
#define db long double
#define il inline

using namespace std;
const int N=1e3+10,mod=1e9+7;
il LL rd()
{
    LL x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int n,a[N][N],a1,a2,sm,b[N][N],c[N][N],st[N],tp;

int main()
{
    n=rd();
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
        {
            a[i][j]=rd();
            sm=(sm+1ll*(n-i+1)*(n-j+1)%mod)%mod;
        }
    for(int h=30;~h;--h)
    {
        bool o=0;
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
            {
                int x=a[i][j]>>h&1;
                o|=x;
                b[i][j]=x?b[i-1][j]+1:0,c[i][j]=!x?c[i-1][j]+1:0;
            }
        if(!o) continue;
        int cn=0,na;
        for(int i=1;i<=n;++i)
        {
            tp=0,na=0;
            for(int j=1;j<=n;++j)
            {
                while(tp&&b[i][st[tp]]>b[i][j]) na=(na-1ll*(st[tp]-st[tp-1])*b[i][st[tp]]%mod+mod)%mod,--tp;
                st[++tp]=j,na=(na+1ll*(st[tp]-st[tp-1])*b[i][st[tp]]%mod)%mod;
                cn=(cn+na)%mod;
            }
        }
        a1=(a1+(1ll<<h)*cn%mod)%mod;
        cn=0;
        for(int i=1;i<=n;++i)
        {
            tp=0,na=0;
            for(int j=1;j<=n;++j)
            {
                while(tp&&c[i][st[tp]]>c[i][j]) na=(na-1ll*(st[tp]-st[tp-1])*c[i][st[tp]]%mod+mod)%mod,--tp;
                st[++tp]=j,na=(na+1ll*(st[tp]-st[tp-1])*c[i][st[tp]]%mod)%mod;
                cn=(cn+na)%mod;
            }
        }
        a2=(a2+(1ll<<h)*(1ll*sm-cn+mod)%mod)%mod;
    }
    printf("%d %d\n",a1,a2);
    return 0; 
}
posted @ 2019-04-24 16:05  ✡smy✡  阅读(171)  评论(0编辑  收藏  举报