【BZOJ5502】[GXOI/GZOI2019]與或和(單調棧)

【BZOJ5502】[GXOI/GZOI2019]與或和(單調棧)

題面

BZOJ
洛谷php

題解

看到位運算就直接拆位,因而問題變成了求有多少個全\(0\)子矩陣和有多少個全\(1\)子矩陣。
這兩個操做本質就是同樣的,不妨考慮有多少個全\(1\)子矩陣。
預處理出每一個元素向上可以找的最多的\(1\)的個數,對於每一行從作往右掃一遍,拿一個單調棧維護一下,這樣子就能夠計算出以每一個元素爲右下角時的貢獻了。
時間複雜度\(O(n^2logV)\),在BZOJ上由於常數太大T了QwQ。ios

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 1010
#define MOD 1000000007
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,a[MAX][MAX],b[MAX][MAX],u[MAX][MAX];
int Q[MAX],h,t,ans1=0,ans2=0;
int main()
{
    n=read();
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)a[i][j]=read();
    for(int k=0;k<31;++k)
    {
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)b[i][j]=(a[i][j]>>k)&1;
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
                if(!b[i][j])u[i][j]=0;
                else u[i][j]=u[i-1][j]+1;
        for(int i=1;i<=n;++i)
        {
            Q[h=t=1]=0;int now=0;
            for(int j=1;j<=n;++j)
            {
                while(h<t&&u[i][Q[t]]>=u[i][j])now=(now+MOD-1ll*(Q[t]-Q[t-1])*u[i][Q[t]]%MOD)%MOD,--t;
                now=(now+1ll*(j-Q[t])*u[i][j])%MOD;Q[++t]=j;
                ans1=(ans1+1ll*now*(1<<k))%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)
                if(!b[i][j])u[i][j]=0;
                else u[i][j]=u[i-1][j]+1;
        ans2=(ans2+1ll*(1<<k)%MOD*(1ll*n*(n+1)/2*n*(n+1)/2%MOD)%MOD)%MOD;
        for(int i=1;i<=n;++i)
        {
            Q[h=t=1]=0;int now=0;
            for(int j=1;j<=n;++j)
            {
                while(h<t&&u[i][Q[t]]>=u[i][j])now=(now+MOD-1ll*(Q[t]-Q[t-1])*u[i][Q[t]]%MOD)%MOD,--t;
                now=(now+1ll*(j-Q[t])*u[i][j])%MOD;Q[++t]=j;
                ans2=(ans2+MOD-1ll*now*(1<<k)%MOD)%MOD;
            }
        }
        continue;
    }
    printf("%d %d\n",ans1,ans2);
    return 0;
}
相關文章
相關標籤/搜索