Luogu P5300 [GXOI/GZOI2019]与或和

又是GXOI2019里水水的送分题,然而我看错题+细节写挂愣是搞了一个多小时(深深的明白自己的弱小)

首先看到二进制我们就考虑到枚举每一位,算子矩阵中这一位为\(1\)的方案数再乘起来吧

然后这时候矩阵就变成了\(0/1\)矩阵(其实样例1就在提示你这种做法),我们考虑\(\operatorname{AND}\)时的答案,即求出\(1\)矩阵个数

这个很套路吧,我们预处理出\(h_{i,j}\)表示\((i,j)\)以上(包括\((i,j)\)最多有多少个连续的\(1\)

接下来考虑求出以每一个点为矩阵的右下角时的方案数,每一列的情况可以单调栈处理:

考虑连续递增的一段的答案(如\((3,5,6)\)我们是可以计算的,就是一个求和的过程),那么我们就维护单调递增的单调栈

然后考虑每次加入一个数,那么前面大于它的都会被他删掉,此时的贡献其实就是删掉数的个数\(\times\)当前的\(h_{i,j}\)

因此单调栈里再维护一下前缀和以及数的个数(因为被删除的要累积到删除它的数上),就完成了\(\operatorname{AND}\)的做法

那么\(\operatorname{OR}\)呢,其实直接补集转化一下就好了,用总方案数减去全\(0\)矩阵方案数即为所求

因此综上,复杂度\(O(n^2\log 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;
}
posted @ 2019-06-03 21:07 hl666 阅读(...) 评论(...) 编辑 收藏