CF1799E 题解
unputdownable · · 题解
提供一种
对于一个联通块,称其合法当且仅当它满足题目所属条件(即任何两个格点之间最短路都等于曼哈顿距离)。
这一段只讨论只有一个联通块的情况。
观察一个合法联通块的边界,从最高点顺时针走,一定是:
- 先只向右向下走;
- 再只向左向下走;
- 再只向左向上走;
- 再只向右向上走。
如果设 . 的数量,则联通块合法当且仅当:
- 对于所有
i 都有L_i \leq L_{i-1} 或者L_i \leq L_{i+1} ; - 对于右侧也满足上述条件;
- 每行
#连续。
考虑一个将不合法的联通块扩充成最小合法联通块的算法:
先算出每行左侧有多少 .;
将
将
令 #)。
不难发现这会得到每个
接着对右侧做同样的事情。
最后将所有未标记格子全部变成 #。(这同时处理了中间的空洞)
然后上面那个算法完全可以处理不连通的情况,但是有时候处理出来的 "最小合法联通块" 可能会不联通。
可以发现这种时候是若干个合法的联通块斜着分布,像这样:
易证直接用最短路把他们联通起来就是合法的。
贴上两段核心代码:
// 处理 L[i] 和 R[i]
L[0]=1; R[0]=m;
L[n+1]=1; R[n+1]=m;
for(int i=1; i<=n; ++i) {
R[i]=0;
while(R[i]<R[i-1]&&s[i][R[i]+1]=='.') vis[i][++R[i]]=1;
L[i]=m+1;
while(L[i]>L[i-1]&&s[i][L[i]-1]=='.') vis[i][--L[i]]=1;
}
for(int i=n; i>=1; --i) {
R[i]=0;
while(R[i]<R[i+1]&&s[i][R[i]+1]=='.') vis[i][++R[i]]=1;
L[i]=m+1;
while(L[i]>L[i+1]&&s[i][L[i]-1]=='.') vis[i][--L[i]]=1;
}
for(int i=1; i<=n; ++i)
for(int u=1; u<=m; ++u)
if(!vis[i][u]) s[i][u]='#';
// 连接两个点
int dx=P2.first >=P1.first ? 1 : -1;
int dy=P2.second>=P1.second ? 1 : -1;
while(P1!=P2) {
if(s[P1.first+dx][P1.second ]=='#'&&P1.first !=P2.first ) P1.first +=dx;
else if(s[P1.first ][P1.second+dy]=='#'&&P1.second!=P2.second) P1.second+=dy;
else if(P1.first!=P2.first) P1.first +=dx,s[P1.first][P1.second]='#';
else P1.second+=dy,s[P1.first][P1.second]='#';
}