UOJ Logo kczno1的博客

博客

51nod 1303 交叉矩阵

2017-12-24 16:14:46 By kczno1

https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1303

所求=总矩阵对数-相离矩阵对数-相包含矩阵对数

总矩阵对数=${矩阵数 \choose 2}$

考虑如何求有多少个全1矩阵。

先枚举右下角的端点$(i,j)$,维护以$(i,j)$为右下角的全1矩阵个数

枚举行$i$,从左到右枚举列$j$

用单调栈维护$k=1..j$到$j$的$up$的$min$ ($up[i][j]$表示点$(i,j)$向上有连续多少个1)

以$(i,j)$为右下角的矩阵个数=$min_k$的和,可以维护。

考虑如何求相包含矩阵对数。

枚举较大的矩阵($n*m$),则较小矩阵有${n+1 \choose 2} * {m+1 \choose 2}$个

所以用类似上面的方法,维护$1*f(m),n*f(m),n^2*f(m)$的和即可。($f(m)为{m+1 \choose 2} 的前缀和$)

考虑如何求相离矩阵对数。

即左右相离+上下相离-左上右下相离-右上左下相离

求出每个点作为四个方向的端点的矩阵个数,之后就轻松解决了。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
#define pb push_back
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,r,l) for(int i=r;i>=l;--i)

const int N=1e3+5,D=1e9+7;
ll mi(ll x,int y=D-2)
{
    ll ans=1;
    while(y)
    {
        if(y&1)ans=ans*x%D;
        x=x*x%D;y>>=1;
    }
    return ans;
}
int n,m;
char b[N][N];
int up[N][N],down[N][N];
ll S1[N],S2[N],f[N],f2[N];
//S1[n]=sigma i; S2[n]=sigma i^2; f[n]=sigma S[i]
ll a[N],h[N];
ll cnt[N][N],cnt1[N][N],cnt2[N][N],cnt3[N][N];
//cnt 右下角 ;cnt1 左下角 ;cnt2 右上角 ;cnt3 左上角 
ll c[N],c1[N];

int main()
{
    freopen("1.in","r",stdin);//freopen("2.out","w",stdout);
    cin>>n>>m;
    rep(i,1,n)scanf("%s",b[i]+1);

    int mn=max(n,m);
    rep(i,1,mn){S1[i]=S1[i-1]+i;f[i]=f[i-1]+S1[i];}
    rep(i,1,mn){S2[i]=S2[i-1]+i*i;(f2[i]=f2[i-1]+S2[i])%D;}

    rep(i,1,n)
    rep(j,1,m) 
    if(b[i][j]=='1')up[i][j]=up[i-1][j]+1;
    per(i,n,1)
    rep(j,1,m) 
    if(b[i][j]=='1')down[i][j]=down[i+1][j]+1;

    ll ans=0;
    //+C2(tot_cnt) 
    //-相包含
    //-横相离-纵相离+左上右下相离+右上左下相离

    //-相包含 
    rep(i,1,n)
    {
        int top=0,ncnt=0;
        //ncnt sigma h_k
        ll s0=0,s1=0,s2=0;
        //s0=sigma f[h_k]
        //s1=sigma (j-k+1)*f[h_k]
        //s2=sigma (j-k+1)^2*f[h_k]
        rep(j,1,m)
        {
            (s2+=2*s1+s0)%=D;
            (s1+=s0)%=D;
            while(h[top]>up[i][j])
            {
                ncnt-=h[top]*(a[top]-a[top-1]);
                (s0-=f[h[top]]*(a[top]-a[top-1]))%=D;
                (s1-=f[h[top]]*(S1[j-a[top-1]]-S1[j-a[top]]))%=D;
                (s2-=f[h[top]]*(S2[j-a[top-1]]-S2[j-a[top]]))%=D;
                --top;
            }
            h[++top]=up[i][j];
            a[top]=j;
            ncnt+=h[top]*(a[top]-a[top-1]);
            (s0+=f[h[top]]*(a[top]-a[top-1]))%=D;
            (s1+=f[h[top]]*(S1[j-a[top-1]]-S1[j-a[top]]))%=D;
            (s2+=f[h[top]]*(S2[j-a[top-1]]-S2[j-a[top]]))%=D;

            cnt[i][j]=ncnt;
            (ans+=s2+s1)%=D;
        }
    }
    (ans*=mi(2))%=D;
    ans=-ans;

        //get cnt2
    rep(i,1,n)
    {
        int top=0,ncnt=0;
        rep(j,1,m) 
        {
            while(h[top]>down[i][j])
            {
                ncnt-=h[top]*(a[top]-a[top-1]);
                --top;
            }
            h[++top]=down[i][j];
            a[top]=j;
            ncnt+=h[top]*(a[top]-a[top-1]);
            cnt2[i][j]=ncnt;
        }
    }

    a[0]=m+1;
    //get cnt1
    rep(i,1,n)
    {
        int top=0,ncnt=0;
        per(j,m,1) 
        {
            while(h[top]>up[i][j])
            {
                ncnt-=h[top]*(a[top-1]-a[top]);
                --top;
            }
            h[++top]=up[i][j];
            a[top]=j;
            ncnt+=h[top]*(a[top-1]-a[top]);
            cnt1[i][j]=ncnt;
        }
    }
    //get cnt3
    rep(i,1,n)
    {
        int top=0,ncnt=0;
        per(j,m,1)
        {
            while(h[top]>down[i][j])
            {
                ncnt-=h[top]*(a[top-1]-a[top]);
                --top;
            }
            h[++top]=down[i][j];
            a[top]=j;
            ncnt+=h[top]*(a[top-1]-a[top]);
            cnt3[i][j]=ncnt;
        }
    }

    //-横相离 
    rep(i,1,n)
    rep(j,1,m) c[j]+=cnt[i][j];
    rep(i,1,n)
    rep(j,1,m) c1[j]+=cnt1[i][j];
    ll sum=0;
    rep(i,1,m)sum+=c[i];
    sum%=D;
//-----  +C2(tot_cnt) 
    (ans+=(sum+1)*sum/2)%=D;
//-----    
    per(i,m,1)
    {
        (sum-=c[i])%=D;
        (ans-=sum*c1[i])%=D;
    }

    //-纵相离 
memset(c,0,sizeof(c));memset(c1,0,sizeof(c1));
    rep(i,1,n)
    rep(j,1,m) c[i]+=cnt[i][j];
    rep(i,1,n)
    rep(j,1,m) c1[i]+=cnt2[i][j];
    sum=0;
    rep(i,1,n)sum+=c[i];
    sum%=D;
    per(i,n,1)
    {
        (sum-=c[i])%=D;
        (ans-=sum*c1[i])%=D;
    }

    //+左上右下相离
    rep(i,1,n)
    rep(j,1,m)cnt[i][j]+=cnt[i][j-1];
    rep(i,1,n)
    rep(j,1,m)(cnt[i][j]+=cnt[i-1][j])%=D;
    rep(i,1,n)
    rep(j,1,m)(ans+=cnt3[i][j]*cnt[i-1][j-1])%=D;

    //+左下右上相离 
    rep(i,1,n)
    per(j,m,1)cnt1[i][j]+=cnt1[i][j+1];
    rep(i,1,n)
    rep(j,1,m)(cnt1[i][j]+=cnt1[i-1][j])%=D;
    rep(i,1,n)
    rep(j,1,m)(ans+=cnt2[i][j]*cnt1[i-1][j+1])%=D;

    cout<<(ans%D+D)%D;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。