CF364E Empty Rectangles

一、题目

点此看题

二、解法

众所周知一维问题变二维难度思维加倍,细节加倍,稍不注意就晕掉了。

以防降智我们先考虑一维的情况,可以类似猫树分治,过中点处理出左边选 \(x\)\(1\) 的边界,右边选 \(y\)\(1\) 的边界,然后把 \(x+y=k\) 合并起来,用简单乘法原理算答案即可。

二维的情况可以使用交替分治,也就是我们切 \(x\) 轴、切 \(y\) 轴一直交替进行。下面我们考虑切 \(x\) 轴的情况:

我们枚举矩形的左边界 \(i\),然后把右边界当成指针一直往右扫,过程中维护"橙线",也就是达到 \(x\)\(1\) 的边界。显然随着右边界的移动上面橙线只会下移,下面的橙线只会上移,这个是具有单调性的,可以维护 \(k\) 个指针,算答案的时候还是简单乘法原理。

现在来分析时间复杂度,设 \(lenx,leny\) 分别表示两维的长度,那么一次计算的复杂度是 \(O(lenx\cdot leny\cdot k+lenx^2)\),由于我们是对两维交替分治,所以时间复杂度 \(O(nmk\log nm)\)

实现小细节:二维问题一定要注意区间的开闭,采用左开右闭可以减少很多细节。

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 2505;
#define ll long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,a[M][M],b[M][M],up[2][10];ll ans;
int cal(int xl,int xr,int yl,int yr)
{
	return b[xr][yr]-b[xr][yl]-b[xl][yr]+b[xl][yl];
}
void cdq(int xl,int xr,int yl,int yr,int ty)
{
	if(xl==xr || yl==yr) return ;
	if(xl+1==xr && yl+1==yr)
	{
		ans+=(cal(xl,xr,yl,yr)==k);
		return ;
	}
	if(ty==0)
	{
		int mid=(xl+xr)>>1;
		cdq(xl,mid,yl,yr,1);
		cdq(mid,xr,yl,yr,1);
		for(int i=yl;i<=yr;i++)
		{
			up[0][0]=up[1][0]=mid;
			for(int j=1;j<=k+1;j++)
				up[0][j]=xl,up[1][j]=xr;
			for(int j=i+1;j<=yr;j++)
			{
				for(int p=1;p<=k+1;p++)
				{
					while(cal(up[0][p],mid,i,j)>=p)
						up[0][p]++;
					while(cal(mid,up[1][p],i,j)>=p)
						up[1][p]--;
				}
				for(int p=0;p<=k;p++)
					ans+=(up[0][p]-up[0][p+1])
					*(up[1][k-p+1]-up[1][k-p]);
			}
		}
	}
	else
	{
		int mid=(yl+yr)>>1;
		cdq(xl,xr,yl,mid,0);
		cdq(xl,xr,mid,yr,0);
		for(int i=xl;i<=xr;i++)
		{
			up[0][0]=up[1][0]=mid;
			for(int j=1;j<=k+1;j++)
				up[0][j]=yl,up[1][j]=yr;
			for(int j=i+1;j<=xr;j++)
			{
				for(int p=1;p<=k+1;p++)
				{
					while(cal(i,j,up[0][p],mid)>=p)
						up[0][p]++;
					while(cal(i,j,mid,up[1][p])>=p)
						up[1][p]--;
				}
				for(int p=0;p<=k;p++)
					ans+=(up[0][p]-up[0][p+1])
					*(up[1][k-p+1]-up[1][k-p]);
			}
		}
	}
}
signed main()
{
	n=read();m=read();k=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			b[i][j]=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
			scanf("%1d",&a[i][j]);b[i][j]+=a[i][j];
		}
	cdq(0,n,0,m,1);
	printf("%lld\n",ans);
}
posted @ 2021-11-06 14:42  C202044zxy  阅读(172)  评论(0编辑  收藏  举报