【Codeforces845E】Fire in the City

题意:
一个火源第 i 秒形成的火区是一个边长为2i+1的正方形
n×m 的网格中,问已知 k 个火源,再加一个之后(由你选),问最少的时间可以将所有区域变为火区。

首先最少时间肯定需要二分mid,然后每个火区的关键点就只有4个。离散化后可以二维差分一下算出最上、下、左、右的四个没有被烧到的关键点。看看和 mid 之间的关系就好了。

#include <bits/stdc++.h>
#define gc getchar()
#define ll long long
#define mid (l+r>>1)
#define inf 0x3f3f3f3f
#define N 509 
using namespace std;
int n,m,k,a[N],b[N];
vector<int> x,y;
vector<int> ::iterator it;
/*map<int,int> x,y;
map<int,int> ::iterator it;*/
int tg[N*2][N*2];
//int X[N*2],Y[N*2],number_x,number_y;
int read()
{
    int x=1;
    char ch;
    while (ch=gc,ch<'0'||ch>'9') if (ch=='-') x=-1;
    int s=ch-'0';
    while (ch=gc,ch>='0'&&ch<='9') s=s*10+ch-'0';
    return x*s;
}
bool check(int lim)
{
    x.clear(),y.clear();
    //number_x=number_y=0;
    for (int i=1;i<=k;i++)
    {
        int Up=max(1,a[i]-lim),Down=min(n,a[i]+lim);
        int Left=max(1,b[i]-lim),Right=min(m,b[i]+lim);
        /*x[Up]=inf,x[Down+1]=inf;
        y[Left]=inf,y[Right+1]=inf;*/
        x.push_back(Up),x.push_back(Down+1);
        y.push_back(Left),y.push_back(Right+1);
    }
    /*x[1]=x[n+1]=inf;
    y[1]=y[m+1]=inf;
    for (it=x.begin();it!=x.end();it++)
        X[++number_x]=(*it).first,(*it).second=number_x;
    for (it=y.begin();it!=y.end();it++)
        Y[++number_y]=(*it).first,(*it).second=number_y;*/
    x.push_back(1),x.push_back(n+1);
    y.push_back(1),y.push_back(m+1);
    sort(x.begin(),x.end());
    sort(y.begin(),y.end());
    x.erase(unique(x.begin(),x.end()),x.end());
    y.erase(unique(y.begin(),y.end()),y.end());
    memset(tg,0,sizeof(tg));
    for (int i=1;i<=k;i++)
    {
        int Up=max(1,a[i]-lim),Down=min(n,a[i]+lim);
        int Left=max(1,b[i]-lim),Right=min(m,b[i]+lim);
        Up=lower_bound(x.begin(),x.end(),Up)-x.begin()+1;
        Down=lower_bound(x.begin(),x.end(),Down+1)-x.begin()+1;
        Left=lower_bound(y.begin(),y.end(),Left)-y.begin()+1;
        Right=lower_bound(y.begin(),y.end(),Right+1)-y.begin()+1;
        /*tg[x[Up]][y[Left]]++,tg[x[Up]][y[Right+1]]--;
        tg[x[Down+1]][y[Left]]--,tg[x[Down+1]][y[Right+1]]++;*/
        tg[Up][Left]++,tg[Up][Right]--;
        tg[Down][Left]--,tg[Down][Right]++;
    }
    int Min_x=inf,Min_y=inf,Max_x=0,Max_y=0;
    for (int i=1;i<(int)x.size();i++)
        for (int j=1;j<(int)y.size();j++)
        {
            tg[i][j]+=tg[i][j-1]+tg[i-1][j]-tg[i-1][j-1];
            if (!tg[i][j])
            {
                Min_x=min(Min_x,i),Max_x=max(Max_x,i);
                Min_y=min(Min_y,j),Max_y=max(Max_y,j);
            }
        }
    if (Min_x==inf) return 1;
    Min_x=x[Min_x-1],Min_y=y[Min_y-1];
    Max_x=x[Max_x]-1,Max_y=y[Max_y]-1;
    return (Max_x-Min_x<=lim*2&&Max_y-Min_y<=lim*2);
}
int main()
{
    n=read(),m=read(),k=read();
    for (int i=1;i<=k;i++) a[i]=read(),b[i]=read();
    int l=0,r=max(n,m)/2,ret=0;
    while (l<=r)
    {
        if (check(mid)) ret=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ret);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值