【Codeforces 364E】Empty Rectangles

二维分治。

题目大意:

给定一个 $n\times m$ 的 01 矩阵,问有多少子矩阵的和为 $k$。

$k$ 非常小。

题解:

考虑分治。假定 $n$ 与 $m$ 同阶。

我们取一条横切当前矩阵的线,并且满足两半的大小相差最小。

然后分别考虑上、下两个部分。我们对于左端点在 $l$,右端点在 $r$ 的所有情况,求出其中一条边紧靠切割线的矩形中,矩形内部和不超过 $0\sim k$ 的矩形个数。

枚举 $l,r,k$,时间复杂度为 $O(n^2k)$。

在 $l$ 固定时,随着 $r$ 的增大,矩形的另外一个边界必然会不断往中间那条线靠近。于是可以用一个指针来搞。

这样单层分治的复杂度就是 $O(n^2k)$。

由于有两个维度,所以我们每次取长度较大的那个维度进行切割,即可保证复杂度(也可以横一刀纵一刀切)。

时间复杂度 $O(n^2k\log n)$。

Code:

#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=2555;
int n,m,k,s[N][N];
char b[N][N];
LL ans;
inline int sum(int r1,int r2,int c1,int c2){
    return s[r2][c2]-s[r2][c1-1]-s[r1-1][c2]+s[r1-1][c1-1];
}
int A[N][N][7],B[N][N][7];
void solve(int l,int r,int L,int R){
    if(l==r&&L==R){
        ans+=k==(b[l][L]^'0');
        return;
    }
    if(r-l+1>R-L+1){
        const int mid=l+r>>1;
        for(int i=L;i<=R;++i)for(int j=i;j<=R;++j)memset(A[i][j],0,sizeof**A),memset(B[i][j],0,sizeof**B);
        for(int K=0;K<=k;++K){
            for(int x=L;x<=R;++x){
                int up=l,down=r;
                for(int y=x;y<=R;++y){
                    while(up<=mid&&sum(up,mid,x,y)>K)++up;
                    while(down>mid&&sum(mid+1,down,x,y)>K)--down;
                    A[x][y][K]=mid-up+1,B[x][y][K]=down-mid;
                }
            }
        }
        for(int x=L;x<=R;++x)for(int y=x;y<=R;++y)for(int u=0;u<=k;++u)ans+=(A[x][y][u]-(u?A[x][y][u-1]:0))*(B[x][y][k-u]-(k-u?B[x][y][k-u-1]:0));
        solve(l,mid,L,R),solve(mid+1,r,L,R);
    }else{
        const int mid=L+R>>1;
        for(int i=l;i<=r;++i)for(int j=i;j<=r;++j)memset(A[i][j],0,sizeof**A),memset(B[i][j],0,sizeof**B);
        for(int K=0;K<=k;++K){
            for(int x=l;x<=r;++x){
                int up=L,down=R;
                for(int y=x;y<=r;++y){
                    while(up<=mid&&sum(x,y,up,mid)>K)++up;
                    while(down>mid&&sum(x,y,mid+1,down)>K)--down;
                    A[x][y][K]=mid-up+1,B[x][y][K]=down-mid;
                }
            }
        }
        for(int x=l;x<=r;++x)for(int y=x;y<=r;++y)for(int u=0;u<=k;++u)ans+=(A[x][y][u]-(u?A[x][y][u-1]:0))*(B[x][y][k-u]-(k-u?B[x][y][k-u-1]:0));
        solve(l,r,L,mid),solve(l,r,mid+1,R);
    }
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m>>k;
    for(int i=1;i<=n;++i)cin>>b[i]+1;
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)
    s[i][j]=(b[i][j]^'0')+s[i-1][j]+s[i][j-1]-s[i-1][j-1];
    solve(1,n,1,m);
    cout<<ans<<'\n';
    return 0;
}