Codeforces364E Empty Rectangles【分治】

题目描述:

给一个n*m的01矩阵,问有多少个子矩阵满足恰有k个1
1<=n,m<=2500,0<=k<=6

题目分析:

分治真是太强了%%%
这种矩形问题,计算方法通常是要定一条边,然后两边限制统计方案。

考虑将矩形(1,n,1,m)划分(比如说按水平线一分为二,设mid=(1+n)/2),预处理出第 i i i列到第 j j j列恰有 t t t个1往上最多能延伸到哪,以及往下最多能延伸到哪,那么跨过mid的矩形就可以枚举上半部分的1的个数 t t t,答案就加上 ( u p [ i ] [ j ] [ t ] − u p [ i ] [ j ] [ t − 1 ] ) ∗ ( d o w n [ i ] [ j ] [ k − t − 1 ] − d o w n [ i ] [ j ] [ k − t ] ) (up[i][j][t]-up[i][j][t-1])*(down[i][j][k-t-1]-down[i][j][k-t]) (up[i][j][t]up[i][j][t1])(down[i][j][kt1]down[i][j][kt])

这样就可以计算跨过mid的矩形,然后分治两边,每次划分长的一边,一层的复杂度是 O ( n m + min ⁡ ( n , m ) 2 k ) O(nm+\min(n,m)^2k) O(nm+min(n,m)2k),总复杂度就是 O ( n m k ∗ l o g ( n m ) ) O(nmk*log(nm)) O(nmklog(nm))吗?其实我不太会分析

预处理的方法可以看代码, : : k ::k ::k的意思是访问全局变量。
Code:

#include<bits/stdc++.h>
#define maxn 2505
using namespace std;
int n,m,k,a[maxn][maxn],v1[maxn][maxn][8],v2[maxn][maxn][8];
char s[maxn];
long long ans;
void Divide(int D,int U,int L,int R){
	if(L==R&&D==U) {ans+=(a[D][L]==k);return;}
	if(U-D>R-L){
		int mid=(U+D)>>1;
		for(int j=L;j<=R;j++){
			int tp1=0,tp2=0;
			for(int i=mid+1;i<=U&&tp1<=k;i++) if(a[i][j]) v1[j][j][++tp1]=i;
			for(int i=mid;i>=D&&tp2<=k;i--) if(a[i][j]) v2[j][j][++tp2]=i;
			while(tp1<=k) v1[j][j][++tp1]=U+1;
			while(tp2<=k) v2[j][j][++tp2]=D-1;
		}
		for(int j=L;j<=R;j++) for(int k=j+1;k<=R;k++){
			int i1=1,i2=1;
			for(int i=1;i<=::k+1;i++) v1[j][k][i]=v1[j][k-1][i1]<v1[k][k][i2]?v1[j][k-1][i1++]:v1[k][k][i2++];
			i1=i2=1;
			for(int i=1;i<=::k+1;i++) v2[j][k][i]=v2[j][k-1][i1]>v2[k][k][i2]?v2[j][k-1][i1++]:v2[k][k][i2++];
		}
		for(int j=L;j<=R;j++) for(int k=j;k<=R;k++) for(int t=0;t<=::k;t++)
			ans+=(v1[j][k][t+1]-(t?v1[j][k][t]:mid+1))*((t<::k?v2[j][k][::k-t]:mid)-v2[j][k][::k-t+1]);
		Divide(D,mid,L,R),Divide(mid+1,U,L,R);
	}
	else{
		int mid=(L+R)>>1;
		for(int i=D;i<=U;i++){
			int tp1=0,tp2=0;
			for(int j=mid+1;j<=R&&tp1<=k;j++) if(a[i][j]) v1[i][i][++tp1]=j;
			for(int j=mid;j>=L&&tp2<=k;j--) if(a[i][j]) v2[i][i][++tp2]=j;
			while(tp1<=k) v1[i][i][++tp1]=R+1;
			while(tp2<=k) v2[i][i][++tp2]=L-1;	
		}
		for(int i=D;i<=U;i++) for(int k=i+1;k<=U;k++){
			int j1=1,j2=1;
			for(int j=1;j<=::k+1;j++) v1[i][k][j]=v1[i][k-1][j1]<v1[k][k][j2]?v1[i][k-1][j1++]:v1[k][k][j2++];
			j1=j2=1;
			for(int j=1;j<=::k+1;j++) v2[i][k][j]=v2[i][k-1][j1]>v2[k][k][j2]?v2[i][k-1][j1++]:v2[k][k][j2++];
		}
		for(int i=D;i<=U;i++) for(int k=i;k<=U;k++) for(int t=0;t<=::k;t++)
			ans+=(v1[i][k][t+1]-(t?v1[i][k][t]:mid+1))*((t<::k?v2[i][k][::k-t]:mid)-v2[i][k][::k-t+1]);
		Divide(D,U,L,mid),Divide(D,U,mid+1,R);
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++){
		scanf("%s",s+1);
		for(int j=1;j<=m;j++) a[i][j]=s[j]-'0';
	}
	Divide(1,n,1,m);
	printf("%lld\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值