E. Fire in the City Educational Codeforces Round 27 二分+扫描线求面积覆盖+离散化 丧心病狂

本文探讨了一个城市火灾蔓延模型问题,通过二分贪心、扫描线及离散化等算法解决复杂场景下最小时间估算问题。

E. Fire in the City
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

The capital of Berland looks like a rectangle of size n × m of the square blocks of same size.

Fire!

It is known that k + 1 blocks got caught on fire (k + 1 ≤ n·m). Those blocks are centers of ignition. Moreover positions of k of these centers are known and one of these stays unknown. All k + 1 positions are distinct.

The fire goes the following way: during the zero minute of fire only these k + 1 centers of ignition are burning. Every next minute the fire goes to all neighbouring blocks to the one which is burning. You can consider blocks to burn for so long that this time exceeds the time taken in the problem. The neighbouring blocks are those that touch the current block by a side or by a corner.

Berland Fire Deparment wants to estimate the minimal time it takes the fire to lighten up the whole city. Remember that the positions of k blocks (centers of ignition) are known and (k + 1)-th can be positioned in any other block.

Help Berland Fire Department to estimate the minimal time it takes the fire to lighten up the whole city.
Input

The first line contains three integers n, m and k (1 ≤ n, m ≤ 109, 1 ≤ k ≤ 500).

Each of the next k lines contain two integers xi and yi (1 ≤ xi ≤ n, 1 ≤ yi ≤ m) — coordinates of the i-th center of ignition. It is guaranteed that the locations of all centers of ignition are distinct.
Output

Print the minimal time it takes the fire to lighten up the whole city (in minutes).
Examples
Input

7 7 3
1 2
2 1
5 5

Output

3

Input

10 5 1
3 3

Output

2

题意 : 给你一个空图 有k+1个着火点, 其中有k个着火点已经给出,剩下的那一个着火点由你来摆放。 着火点每一秒向外扩充一圈。 问你最少多少秒能使整个图全部被点着。

part 1

首先,我们先考虑最简单的问题, 如果这个图是一维的,那么就是个经典的二分贪心模型题。

part2

再者,在不考虑数据范围的前提下想一下这题的解法,原本是一维中是求每个点可以覆盖的长度。 现在是求每个点可以覆盖的面积。 可以(看)联(题)想(解)得出,解决这个问题可以使用扫描线(扫描线可以解决面积覆盖问题)

part 3

最后,看到图的范围丧心病狂的达到了1e9*1e9 但是着火点最多只有500个 。 这种数据范围是很明显的离散化模型题。

part4

思路贼清晰,就是几个简单的模型凑在一起。 。
but!@ 我写了好久无法实现。。。。 太菜了。
最后去cf上膜了别人的代码后才知道怎么写。
二分的判断函数可以这么实现
一、 处理完所有直线 每条直线弄成[ l , r ) 这种形式,并将端点加入哈希表,上界权值设为-1 下界权值设为1
二、离散化,离散化之后,我们考虑离散化后每个点的实际意义。
每个点不仅仅是一个点了,他可能代表一条线段。 所以才要将线段写成半闭半开区间的形式。 离散化要注意一点 为了保证离散化的正确性,我们需要加入参照物,既边界。
三. 定义四个变量 分别代表第k+1个着火点必须点燃的部分的边界。
这个边界如何更新呢? 当我们每扫完一层线段后 我们进行一次遍历,
检测是否有区间没有被覆盖。 对于没有覆盖的区间,边界必须将其包含。
四, 最后检查 如果边界差值大于2*d 则说明这个d肯定不合法,反之则合法。

具体实现见代码。 嗯 代码确实丑了点

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
#define ll long long
#define M 1000000007
#define all(a) a.begin(), a.end()
class line
{
public:
    int h,l,r,s;// 高度 左边界 右边界 上下界
    line(int h,int l,int r,int s):h(h),l(l),r(r),s(s){}
    bool operator < (const line &b) const
    {
        return h<b.h;
    }
};
int x[1005],y[1005];
vector <line> VL;// 存扫描线
vector <int> VH;// 存哈希表
int cnt[1005];
int n,m,k;
bool check(int d)
{
    VL.clear();
    VH.clear();
    VH.push_back(1),VH.push_back(m+1);
    VL.push_back(line(1,1,1,1));
    VL.push_back(line(n+1,1,1,1));//  防止越界 添加俩无用边
    for(int i=1;i<=k;i++)
    {
        VL.push_back(line(max(1, x[i] - d), max(1, y[i] - d), min(y[i] + d + 1, m + 1), 1));
        VL.push_back(line(min(n + 1, x[i] + d + 1), max(1, y[i] - d), min(y[i] + d + 1, m + 1), -1));
        VH.push_back(max(1, y[i] - d));
        VH.push_back(min(y[i] + d + 1, m + 1));
    }
    sort(VH.begin(),VH.end());
    VH.erase(unique(VH.begin(),VH.end()),VH.end());
    sort(VL.begin(),VL.end());
    int L=1<<30,R=-1,U=-1,D=1<<30;
    memset(cnt,0,sizeof cnt);
    for(int i=0,j=0;i<VL.size();i=j)
    {
        while(j<VL.size() && VL[i].h==VL[j].h)
        {
            int l=lower_bound(VH.begin(),VH.end(),VL[j].l)-VH.begin();
            int r=lower_bound(VH.begin(),VH.end(),VL[j].r)-VH.begin();
            for(int k= l;k<r;k++) cnt[k]+=VL[j].s;
            j++;
        }
        if(VL[i].h<=n)
        {
            for(int k=0;k<VH.size()-1;k++)
            {
                if(cnt[k]<=0)
                {
                    U=max(U,VH[k+1]-1);
                    D=min(D,VH[k]);
                    R=max(R,VL[j].h-1);
                    L=min(L,VL[i].h);
                }
            }
        }
    }return R - L <= 2 * d && U - D <= 2 * d;
}
int main()
{
    //freopen("input.txt","r",stdin);
    scanf("%d %d %d",&n,&m,&k);
    for(int i=1;i<=k;i++)
    {
        scanf("%d %d",&x[i],&y[i]);
    }
    int l=-1,r=max(n,m);
    while(l<r)
    {
        int d=(l+r)>>1;
        if(check(d))
            r=d;
        else l=d+1;
    }
    cout<<l<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值