[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);
}
View Code

 

posted @ 2019-05-12 17:07  hfctf0210  阅读(255)  评论(0编辑  收藏  举报