1 条题解
-
0
自动搬运
来自洛谷,原作者为

strapplE
宁教我负天下人!搬运于
2025-08-24 22:04:38,当前版本为作者最后更新于2025-05-11 10:22:46,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
很神秘的一个题啊。感觉真可以上位紫或者黑了。
下面我们称
X为非障碍点,_为障碍点。首先做一些 trivial 的转化:多加矩形一定不会变劣,因此我们要求,所有非障碍点,都要有一个 的无障碍矩形包含它。因此 合法当且仅当每个非障碍点都对 合法。接下来还能观察到,显而易见的是,设 为最大的 使得 合法,不存在则为 。那么因为 合法可以直接得到 和 合法,因此 单调不增。因此直接暴力,你可以双指针去做,我们就可以 了。
但是你发现刚才的想法不太容易扩展。这只能说明我们有的性质还不太够,因此在下面,你要观察到一个最重要,也最神经的结论:
对一个固定的 ,如果一个点的"包围圈"都合法,它自然就合法了。
刚才是自然语言或者说是感性理解,接下来我们严谨地刻画一下:对一个非障碍点 ,找到包含它的极大的无障碍四连通块。如果靠在四连通块边上的点(这种点需要满足,和一个障碍点或界外相邻)都合法,则 合法。其实这个结论比刚才说的包围圈弱一些,不过至少我会证,而且下面说的做法也只依赖于这个性质。
这个怎么理解呢?
首先你可以手摸一下,发现 四周如果都是合法非障碍,则 也合法。还可以做一些加强:如果三边都合法,则 也合法(这个证明并不困难,就不说了)。我们可以在这个感觉的基础上加以证明:
现在所有边上的点都已经合法了,要证明中间的也要合法。从上往下归纳。设目前考虑到的为 。
第一点:很容易发现,如果 (其中 )为障碍点,则取 最小的一个,那么因为 合法,它的矩形一定包含 ,所以 合法。同理 和 也是。下面,我们只需要证明,蓝色部分都非障碍的基础上, 合法即可。

接下来,第二点:如果 为障碍点,取 最小的一个,那么 合法,它的矩形如果包含 就结束了,否则下边界为 ,而且此时 这一行都非障碍,导出 也合法,因此只需考虑 非障碍的情况即可。
以此类推,蓝色会越来越多:

最后整个 的部分都蓝了,那么 显然合法了。

好了,话说这么多,就是为了证明一个结论:对于每个 ,只考虑边上的点的合法性和考虑所有的合法性是等价的。
那么怎么完全解决这个题呢?其实就容易多了。
对每个边上的点,我们把障碍转到它下面(对不同的障碍,共四种不同角度的转法,都要试一次),则它的矩形只能往上左右扩展。枚举它的行 ,设 表示 往上能走多远。则对一个下面是障碍的非障碍位置,可以形成若干个极大矩形,都取个 就行了。
然而这样复杂度还是三方。你会发现,很多区间是没啥用的,只需要关注"极长"的段即可。这部分我的解决方法是建小根笛卡尔树,只需要考虑它的所有子树。不过可能有更容易的做法,但是 anyway,时间复杂度是 ,空间可以做到除读入外 。下面给了一种实现:
#include<bits/stdc++.h> using namespace std; char ss[2505][2505]; int a[2505][2505],b[2505][2505]; int h[2505],d[2505],s[2505],ans[2505]; void get(int rr,int cc){ ans[rr+1]=min(ans[rr+1],cc); } int ls[2505],rs[2505],st[2505],L[2505],R[2505]; void dfs(int x){ if(ls[x]){ dfs(ls[x]);L[x]=L[ls[x]]; if(s[R[ls[x]]]>s[L[ls[x]]-1])get(d[x],R[ls[x]]-L[ls[x]]+1); } if(rs[x]){ dfs(rs[x]);R[x]=R[rs[x]]; if(s[R[rs[x]]]>s[L[rs[x]]-1])get(d[x],R[rs[x]]-L[rs[x]]+1); } } void sfd(int x){ if(ls[x]){ sfd(ls[x]);L[x]=L[ls[x]]; if(s[R[ls[x]]]>s[L[ls[x]]-1])get(R[ls[x]]-L[ls[x]]+1,d[x]); } if(rs[x]){ sfd(rs[x]);R[x]=R[rs[x]]; if(s[R[rs[x]]]>s[L[rs[x]]-1])get(R[rs[x]]-L[rs[x]]+1,d[x]); } } int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;++i)scanf("%s",ss[i]+1); for(int i=0;i<=n;++i)ans[i]=m; for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){ a[i][j]=b[j][i]=(ss[i][j]=='X'); } for(int i=1;i<=m;++i)h[i]=0; for(int i=1;i<=n;++i){ for(int j=1;j<=m;++j){ h[j]=a[i][j]*(h[j]+1),s[j]=s[j-1]+(a[i][j]&&!a[i+1][j]); d[j]=(a[i][j]?h[j]:n+1);if(s[j]>s[j-1])get(h[j],0); ls[j]=rs[j]=0;d[j]=h[j]; } int tt=0; for(int j=1;j<=m;++j){ int lst=0; while(tt&&d[j]<=d[st[tt]])lst=st[tt],--tt; rs[st[tt]]=j;ls[j]=lst;st[++tt]=j; L[j]=R[j]=j; } dfs(st[1]); } for(int i=1;i<=m;++i)h[i]=0; for(int i=n;i>=1;--i){ for(int j=1;j<=m;++j){ h[j]=a[i][j]*(h[j]+1),s[j]=s[j-1]+(a[i][j]&&!a[i-1][j]); d[j]=(a[i][j]?h[j]:n+1);if(s[j]>s[j-1])get(h[j],0); ls[j]=rs[j]=0;d[j]=h[j]; } int tt=0; for(int j=1;j<=m;++j){ int lst=0; while(tt&&d[j]<=d[st[tt]])lst=st[tt],--tt; rs[st[tt]]=j;ls[j]=lst;st[++tt]=j; L[j]=R[j]=j; } dfs(st[1]); } for(int i=1;i<=n;++i)h[i]=0; for(int i=1;i<=m;++i){ for(int j=1;j<=n;++j){ h[j]=a[j][i]*(h[j]+1),s[j]=s[j-1]+(a[j][i]&&!a[j][i+1]); d[j]=(a[j][i]?h[j]:m+1);if(s[j]>s[j-1])get(0,h[j]); ls[j]=rs[j]=0;d[j]=h[j]; } int tt=0; for(int j=1;j<=n;++j){ int lst=0; while(tt&&d[j]<=d[st[tt]])lst=st[tt],--tt; rs[st[tt]]=j;ls[j]=lst;st[++tt]=j; L[j]=R[j]=j; } sfd(st[1]); } for(int i=1;i<=n;++i)h[i]=0; for(int i=m;i>=1;--i){ for(int j=1;j<=n;++j){ h[j]=a[j][i]*(h[j]+1),s[j]=s[j-1]+(a[j][i]&&!a[j][i-1]); d[j]=(a[j][i]?h[j]:m+1);if(s[j]>s[j-1])get(0,h[j]); ls[j]=rs[j]=0;d[j]=h[j]; } int tt=0; for(int j=1;j<=n;++j){ int lst=0; while(tt&&d[j]<=d[st[tt]])lst=st[tt],--tt; rs[st[tt]]=j;ls[j]=lst;st[++tt]=j; L[j]=R[j]=j; } sfd(st[1]); } int mn=-1,r=0,c=0; for(int i=1;i<=n;++i){ ans[i]=min(ans[i-1],ans[i]); if(mn<i*ans[i])mn=i*ans[i],r=i,c=ans[i]; } printf("%d %d\n",r,c); return 0; }
- 1
信息
- ID
- 5056
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者