CF413E 题解

题意简述

有一个 2×n(1n2×105) 的方格迷宫,有一些方格不能走。m 次询问,每次询问迷宫中两个格子是否互相可达及其最短路长度。

题目分析

虽然没有修改,但是用线段树维护答案可能会更直观、清晰一些。

用线段树的每个结点维护第 lr 列这段 2×(rl+1) 的方格左上到右上、左上到右下、左下到右上、左下到右下的最短路的值。实现时为了方便更新、计算,采用一个 2×2 的二维数组。

两个结点答案的合并也比较简单:对 4 个最短路的值,只需要计算一下左结点对应起点经过中间的上行到右节点对应终点以及经过下行的答案取一个 min 即可。具体实现看代码。

代码实现

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;//最大值表示不可达
int n,q,l,r,ans;
char op1[200010],op2[200010];
struct node
{
int l,r;
int mn[2][2];//两维分别是左和右,0 代表上,1 代表下。
friend node operator +(node a,node b)//合并
{
node c;
memset(c.mn,0x3f,sizeof c.mn);//初始化最大值
c.l=a.l,c.r=b.r;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)//枚举中间是从哪行走
c.mn[i][j]=min(c.mn[i][j],a.mn[i][k]+b.mn[k][j]+1); //答案取 min(注意从左结点到右结点时步数要再 +1)
return c;
}
}tr[800010];
void pushup(int p)
{
tr[p]=tr[p<<1]+tr[p<<1|1];
}//左右子结点更新父节点
void build(int p,int l,int r)
{
tr[p].l=l,tr[p].r=r;
if(l==r)
{
tr[p].mn[0][0]=(op1[l]=='.'?0:INF);
tr[p].mn[1][1]=(op2[l]=='.'?0:INF);//如果能走就是 0,否则不可达
tr[p].mn[0][1]=(op1[l]=='.'&&op2[l]=='.'?1:INF);
tr[p].mn[1][0]=(op1[l]=='.'&&op2[l]=='.'?1:INF);//如果能走就是 1,否则不可达
return;
}
int mid=l+r>>1;
build(p<<1,l,mid);//建左子结点
build(p<<1|1,mid+1,r);//建右子结点
pushup(p);
}
node query(int p,int l,int r)//为了方便,直接返回整个结构体。
{
if(tr[p].l>=l&&tr[p].r<=r)
return tr[p];//全覆盖就直接返回。
int mid=tr[p].l+tr[p].r>>1;
if(mid<l)
return query(p<<1|1,l,r);//只涉及右子结点
else if(mid>=r)
return query(p<<1,l,r);//只涉及左子结点
else
return query(p<<1,l,r)+query(p<<1|1,l,r);//左右子结点都涉及,合并一下
}
int main()
{
scanf("%d%d%s%s",&n,&q,op1+1,op2+1);
build(1,1,n);
while(q--)
{
scanf("%d%d",&l,&r);
if((l%n?l%n:n)>(r%n?r%n:n))//注意 n 在上行,2n 在下行
swap(l,r);
printf("%d\n",(ans=query(1,(l%n?l%n:n),(r%n?r%n:n)).mn[(l-1)/n][(r-1)/n])==INF?-1:ans);//注意最大值是不可达,输出 -1
}
return 0;
}
posted @   Hadtsti  阅读(9)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示