[JZOJ5714]矩阵

来源:互联网 发布:源码下载站网站源码 编辑:程序博客网 时间:2024/06/05 17:04

题目大意

求01网格图多少面积>=k的矩阵全0。

做法

预处理每个点往上延伸的长度up[i,j]。
对于每一行,我们顺序扫并维护单调栈。
弹出元素时考虑贡献。
假如弹出第k列,做到第l列,栈中上一个位置在第j列。
则得到一个高为up[i,k],长为l-j-1的矩形。
在这个矩形里求面积>=k的全0子矩阵个数(下边界必须是i)。
为了不计重,这个子矩阵的高要>max(up[i,j],up[i,l])。
考虑解决这样一个问题。
长为c,高要>b,最高是a+b。
假如选了一个(a’+b)*c’的,其中1<=a’<=a,1<=c’<=c。
那么(a+b)c>=k
a>=kcb
当c’比较小时,若导致kcb>a,贡献为0。
当c’比较大时,若导致kcb<1,贡献为a*(c-c’+1)。
通过二分或预处理得到这两个分界点,然后统计即可。
在正常区间内,会贡献(a+bkc+1)(cc+1)
只要预处理一些前缀和即可统计。
复杂度O(nm)。

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)using namespace std;typedef long long ll;const int maxn=4000+10;int up[maxn][maxn],a[maxn][maxn],ce[maxn];ll sum[maxn],num[maxn];int sta[maxn];int i,j,k,l,t,n,m,tot,top;ll ans;void solve(int a,int b,int c){    int l,r,mid,lc,rc;    if (ce[1]-b>a){        l=1;r=c;        while (l<r){            mid=(l+r+1)/2;            if (ce[mid]-b>a) l=mid;else r=mid-1;        }        lc=l+1;    }    else lc=1;    if (ce[c]-b<1){        l=lc;r=c;        while  (l<r){            mid=(l+r)/2;            if (ce[mid]-b<1) r=mid;else l=mid+1;        }        rc=l-1;        ans+=(ll)a*(c+1)*(c-l+1);        ans-=(ll)a*(l+c)*(c-l+1)/2;    }    else rc=c;    ans+=(ll)(a+b+1)*(c+1)*(rc-lc+1);    ans-=(ll)(c+1)*(num[rc]-num[lc-1]);    ans-=(ll)(a+b+1)*(lc+rc)*(rc-lc+1)/2;    ans+=(ll)(sum[rc]-sum[lc-1]);}int main(){    freopen("matrix.in","r",stdin);freopen("matrix.out","w",stdout);    scanf("%d%d%d",&n,&m,&k);    fo(i,1,n){        if (k%i==0) t=k/i;else t=k/i+1;        num[i]=ce[i]=t;        sum[i]=(ll)t*i;        num[i]+=num[i-1];        sum[i]+=sum[i-1];    }    fo(i,1,n)        fo(j,1,m)            scanf("%d",&a[i][j]);    fo(i,1,n)        fo(j,1,m)            if (a[i][j]) up[i][j]=0;else up[i][j]=up[i-1][j]+1;    fo(i,1,n){        top=0;        fo(j,1,m+1){            while (top&&up[i][j]<=up[i][sta[top]]){                t=max(up[i][sta[top-1]],up[i][j]);                solve(up[i][sta[top]]-t,t,j-sta[top-1]-1);                top--;            }            sta[++top]=j;        }    }    printf("%lld\n",ans);}
原创粉丝点击