E. Arson In Berland Forest
The Berland Forest can be represented as an infinite cell plane. Every cell contains a tree. That is, contained before the recent events.

A destructive fire raged through the Forest, and several trees were damaged by it. Precisely speaking, you have a You are sure that all burnt trees are shown on the map. All the trees outside the map are undamaged.

The firemen quickly extinguished the fire, and now they are investigating the cause of it. The main version is that there was an arson: at some moment of time (let's consider it as T, the fire was extinguished.

The firemen want to find the arsonists as quickly as possible. The problem is, they know neither the value of T (to know how far could the arsonists escape) and a possible set of trees that could be initially set on fire.

Note that you'd like to maximize value T but the set of trees can be arbitrary.

Input

The first line contains two integer 1≤n⋅m≤106) — the sizes of the map.

Next 

It's guaranteed that the map contains at least one "X".

Output

In the first line print the single integer 

Examples
input
Copy
3 6
XXXXXX
XXXXXX
XXXXXX
output
Copy
1
......
.X.XX.
......
input
Copy
10 10
.XXXXXX...
.XXXXXX...
.XXXXXX...
.XXXXXX...
.XXXXXXXX.
...XXXXXX.
...XXXXXX.
...XXXXXX.
...XXXXXX.
..........
output
Copy
2
..........
..........
...XX.....
..........
..........
..........
.....XX...
..........
..........
..........
input
Copy
4 5
X....
..XXX
..XXX
..XXX
output
Copy
0
X....
..XXX
..XXX
..XXX

题意:给一个n*m<=1e6的图,"X"代表着火,"."代表没着火,火只在这个范围里面蔓延。着火的格子每回合会蔓延到周围的8个格子,给出了最后的火情图,求着火最多多少回合,并构造刚着火的图。

想法:很明显蔓延后的火都是一个矩形,所以我先横着遍历1遍,再竖着遍历遍,基本可以确认这个火最多蔓延了多少回合,构造图通过前缀和来判断,比较简单。然后我就被这组数据卡住了

7 5
..XXX
..XXX
..XXX
.XXX.
XXX..
XXX..
XXX..

我一开始的输出
1
.....
...X.
.....
.....
.....
.X...
.....

答案
0
..XXX
..XXX
..XXX
.XXX.
XXX..
XXX..
XXX..

 被这个数据卡住是因为,横着扫一遍竖着扫一遍只能确定最多蔓延回合不会超过这个数值,但是构造出来的图不一定能蔓延回原来的图。

做法:二分答案,先记录终图的前缀和,然后构造起始图,该点为"X"意味着终图中以该点为中心的m范围全为"X",用二维前缀和来判断。否则为"."。构造的同时记录起始图的前缀和。

构造完起始图后我们再检查初始是否合法,同样是用前缀和,该点为"X"意味着起始图中以该点为中心的m范围有"X"或者该点为"."意味着起始图中以该点为中心的m范围。

分析复杂度,每次二分我们需要构造1次和检查1次,复杂度是2e6。对于1e6的图需要二分20次复杂度是4e7,分析可得,蔓延最大不会超过1000回合,只需二分10次,复杂度变2e7。而横着扫一遍竖着扫一遍可以确定二分上界,这个操作复杂度是2e6,这样比纯二分快点。。

#include <bits/stdc++.h>
using namespace std;
const int N=2e6+5;
char p[N];
char v[N];
int u[N];
int z[N];
int n,m;
inline bool solve(int zd)
{
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            int t=i*(m+1)+j;
            v[t]='.';
            if(p[t]=='X')
            {
                int flag=0;
                int rx=i+zd;
                int ry=j+zd;
                int lx=i-zd;
                int ly=j-zd;
                if(rx>n||ry>m||lx<=0||ly<=0)flag=1;
                if((u[rx*(m+1)+ry]-u[rx*(m+1)+ly-1]-u[(lx-1)*(m+1)+ry])+u[(lx-1)*(m+1)+ly-1]!=((zd*2+1)*(zd*2+1)))flag=1;
                if(!flag)v[t]='X';
            }
            z[t]=(v[t]=='X'?1:0)+z[t-1]+z[t-m-1]-z[t-m-2];
        }
    }
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            int t=i*(m+1)+j;
            if(p[t]=='X')
            {
                int rx=i+zd;
                int ry=j+zd;
                int lx=i-zd;
                int ly=j-zd;
                if(rx>n||ry>m||lx<=0||ly<=0)continue;
                if((z[rx*(m+1)+ry]-z[rx*(m+1)+ly-1]-z[(lx-1)*(m+1)+ry]+z[(lx-1)*(m+1)+ly-1])==0)return false;
            }
        }
    }
    return true;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            int t=i*(m+1)+j;
            scanf(" %c",&p[t]);
            u[t]=(p[t]=='X'?1:0)+u[t-1]+u[t-m-1]-u[t-m-2];
        }
    }
    int zd=10000005;
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            int t=0;
            while(p[i*(m+1)+j]=='X')
            {
                j++;
                t++;
                if(j==m+1)break;
            }
            if(t&&zd>t)zd=t;
        }
    }
    for(int i=1;i<=m;++i)
    {
        for(int j=1;j<=n;++j)
        {
            int t=0;
            while(p[i+j*(m+1)]=='X')
            {
                j++;
                t++;
                if(j==n+1)break;
            }
            if(t&&zd>t)zd=t;
        }
    }
    zd=(zd-1)/2;
    if(zd==0)
    {
        printf("0\n");
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=m;++j)printf("%c",p[i*(m+1)+j]);
            printf("\n");
        }
        return 0;
    }
    int l=0,r=zd;
    int ans=0;
    while(l<=r)
    {
        int m=(l+r)>>1;
        if(solve(m))
        {
            l=m+1;
            ans=m;
        }
        else r=m-1;
    }
    printf("%d\n",ans);
    solve(ans);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)printf("%c",v[i*(m+1)+j]);
        printf("\n");
    }
    return 0;
}
代码

总结:直接开二维数组不可取,我是用了一维的来表示二维的,用vector应该会方便不少。然后就是要多练,此外对题目的复述要准确,问人时完全描述成了另一道题。。这都是值得努力的地方

相关文章:

  • 2021-07-05
  • 2022-03-07
  • 2021-08-04
  • 2021-07-29
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2022-02-25
  • 2022-02-10
  • 2021-07-06
  • 2022-01-03
  • 2021-06-16
  • 2021-10-16
  • 2022-01-06
相关资源
相似解决方案