LG5300 「GZOI2019/GXOI2019」与或和 二进制+单调栈

问题描述

Freda 学习了位运算和矩阵以后,决定对这种简洁而优美的运算,以及蕴含深邃空间的结构进行更加深入的研究。

对于一个由非负整数构成的矩阵,她定义矩阵的 \(\texttt{AND}\) 值为矩阵中所有数二进制 \(\texttt{AND(&)}\) 的运算结果;定义矩阵的 \(\texttt{OR}\) 值为矩阵中所有数二进制 \(\texttt{OR(|)}\) 的运算结果。

给定一个 \(N \times N\) 的矩阵,她希望求出:

  1. 该矩阵的所有子矩阵的 \(\texttt{AND}\) 值之和(所有子矩阵 \(\texttt{AND}\) 值相加的结果)。
  2. 该矩阵的所有子矩阵的 \(\texttt{OR}\) 值之和(所有子矩阵 \(\texttt{OR}\) 值相加的结果)。

接下来的剧情你应该已经猜到——Freda 并不想花费时间解决如此简单的问题,所以这个问题就交给你了。

由于答案可能非常的大,你只需要输出答案对 \(1,000,000,007 (10^9 + 7)\) 取模后的结果。

LG5300


题解

发现 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();
}
上一篇:【题解】洛谷 P2725 邮票 Stamps


下一篇:我的OI目标