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
",a1,a2);
    return 0; 
}
原文地址:https://www.cnblogs.com/smyjr/p/10762929.html