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);
}