Codeforces 845E Fire in the City

原题

Codeforces845E

题意

一个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;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值