原题
题意
一个n*m的矩阵,矩阵中初始有k+1个点被标记,其中k个点是已知的。
随着时间t的增加,被标记点会将它周围九宫格区域内的点标记,被标记点会继续标记其他点。
在其中一个初始点未知的情况下,求最小的t使矩阵内所有点都被标记。
解题思路
对于一个初始点( x , y ),在时间t内直接或间接标记的点( x0 , y0 )的范围应该是x-t<=x0<=x+t , y-t<=y0<=y=t。
假设我们已经知道,在当前时间t的情况下矩阵内所有点都被标记了。
我们将被已知的k个点标记的点记为A类点,矩阵内的其他点为B类点。
那么所有B类点都需要被未知的那个点覆盖。因此我们就可以得到一个能把所有B类点覆盖的最小的矩阵( xl , yl , xr , yr )。如果要用一个点将这个矩阵覆盖,那么最小的t’肯定是( max( xr - xl , yr - yl )+1 ) / 2(整除)(加1是因为考虑到了max ( xr - xl , yr - yl )为奇数的情况)。
如果t’<=t,那在时间t内覆盖整个矩形就是可行的。
而且由于时间t具有单调性,所以我们可以二分判断。
因为我们需要找到覆盖所有B类点的最小的矩阵,易知这个矩阵的位置只与x坐标最大、最小的点和y坐标最大、最小的点有关。
在找B类点时,用线段树维护扫描线;因为区间过大,所以用了线段树动态开点。
对于每一列,如果存在B类点,找到y坐标最大、最小的B类点,并且用当前列的x坐标更新最大、最小的x坐标。
如果x坐标最小的扫描线的x坐标为xi,那么1 <= x < xi的所有点都是B类点,对于x坐标最大的扫描线也是同理。
代码
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=510,Node=5e5+10;
struct Line
{
int x,l,r,op;
bool operator < ( const Line &A ) const { return x<A.x; }
Line (int x=0,int l=0,int r=0,int op=0):x(x),l(l),r(r),op(op){}
}L[N<<1];
int x[N],y[N],n,m,k;
void Init()
{
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<=k;++i)
scanf("%d%d",&x[i],&y[i]);
}
struct SegmentTree
{
#define ls son[z][0]
#define rs son[z][1]
int root,node,son[Node][2],Min[Node],lazy[Node];
int newnode()
{
++node;
son[node][0]=son[node][1]=0;
Min[node]=lazy[node]=0;
return node;
}
void Setlazy(int z,int v)
{
Min[z]+=v;
lazy[z]+=v;
}
void Update(int z) { Min[z]=min(Min[ls],Min[rs])+lazy[z]; }
void Add(int &z,int l,int r,int ql,int qr,int v)
{
if (!z) z=newnode();
if (ql<=l && r<=qr) { Setlazy(z,v); return ; }
int mid=(l+r)>>1;
if (ql<=mid) Add(ls,l,mid,ql,qr,v);
if (qr>mid) Add(rs,mid+1,r,ql,qr,v);
Update(z);
}
int Queryleft(int z,int l,int r,int slazy)
{
if (!z || l==r) return l;
int mid=(l+r)>>1;
slazy+=lazy[z];
if (Min[ls]+slazy) return Queryleft(rs,mid+1,r,slazy);
else return Queryleft(ls,l,mid,slazy);
}
int Queryright(int z,int l,int r,int slazy)
{
if (!z || l==r) return r;
int mid=(l+r)>>1;
slazy+=lazy[z];
if (Min[rs]+slazy) return Queryright(ls,l,mid,slazy);
else return Queryright(rs,mid+1,r,slazy);
}
}St;
bool Can(int t)
{
int top=0;
for (int i=1;i<=k;++i)
{
int xl=max(1,x[i]-t),xr=min(n,x[i]+t),yl=max(1,y[i]-t),yr=min(m,y[i]+t);
L[++top]=Line(xl,yl,yr,1); L[++top]=Line(xr+1,yl,yr,-1);
}
sort(L+1,L+top+1);
int xl=n+1,xr=0,yl=m+1,yr=0;
if (L[1].x>1) xl=1,xr=L[1].x-1,yl=1,yr=m;
St.root=St.node=0;
for (int i=1;i<=top;)
{
int pos=i;
while (L[pos].x==L[i].x && i<=top)
St.Add(St.root,1,m,L[i].l,L[i].r,L[i].op),++i;
if (L[pos].x<=n && St.Min[St.root]==0)
{
xl=min(L[pos].x,xl),xr=max(L[pos].x,xr);
yl=min(yl,St.Queryleft(St.root,1,m,0)); yr=max(yr,St.Queryright(St.root,1,m,0));
}
}
if (L[top].x<n) xr=max(xr,n),xl=min(xl,L[top].x),yl=1,yr=m;
if ((max(xr-xl,yr-yl)+1)/2<=t) return 1;
else return 0;
}
void Solve()
{
int l=0,r=1e9+1;
while (l<=r)
{
int mid=(l+r)>>1;
if (Can(mid)) r=mid-1;
else l=mid+1;
}
printf("%d\n",l);
}
int main()
{
Init();
Solve();
return 0;
}