CF413E Maze 2D 题解

分析

两眼线段树题。

对于从第 l 列走到第 r 列,我们的出发、到达情况共有 4 种,分别是:

  • l 列第 1 行到 r 列第 1 行。
  • l 列第 1 行到 r 列第 2 行。
  • l 列第 2 行到 r 列第 1 行。
  • l 列第 2 行到 r 列第 2 行。

这个很显然能用线段树来维护。我们令区间 [l,r] 的第 i 种情况下最短路长度为 tr[now].winow 是区间为 [l,r] 对应线段树结构体的下标)。可以得到 push_up 函数:

il void up(int now){
	tr[now].w1=min(tr[now<<1].w1+tr[now<<1|1].w1+work(1,tr[now<<1].r,1,tr[now<<1|1].l),tr[now<<1].w2+tr[now<<1|1].w3+work(2,tr[now<<1].r,2,tr[now<<1|1].l));
	tr[now].w2=min(tr[now<<1].w1+tr[now<<1|1].w2+work(1,tr[now<<1].r,1,tr[now<<1|1].l),tr[now<<1].w2+tr[now<<1|1].w4+work(2,tr[now<<1].r,2,tr[now<<1|1].l));
	tr[now].w3=min(tr[now<<1].w4+tr[now<<1|1].w3+work(2,tr[now<<1].r,2,tr[now<<1|1].l),tr[now<<1].w3+tr[now<<1|1].w1+work(1,tr[now<<1].r,1,tr[now<<1|1].l));
	tr[now].w4=min(tr[now<<1].w4+tr[now<<1|1].w4+work(2,tr[now<<1].r,2,tr[now<<1|1].l),tr[now<<1].w3+tr[now<<1|1].w2+work(1,tr[now<<1].r,1,tr[now<<1|1].l));
}

这里讨论每个大区间对应 2 个子区间的情况就行了。例如 tr[now].w1,有两种子区间的情况:

  • 左子区间从左端点第 1 行走到右端点第 1 行,左子区间右端点第 1 行走到右子区间左端点第 1 行,右子区间左端点第 1 行走到右子区间右端点第 1 行。

  • 左子区间从左端点第 1 行走到右端点第 2 行,左子区间右端点第 2 行走到右子区间左端点第 2 行,右子区间左端点第 2 行走到右子区间右端点第 1 行。

其实在中间跳区间的那一步也可以向左下、右上走,但步数明显更劣,故不考虑。

对于询问操作,如果当前的区间不完全被询问区间包括,则有 3 种情况:

  • 左右子区间都包含询问区间。

  • 只有左子区间包含询问区间。

  • 只有右子区间包含询问区间。

对于情况 2,3,我们直接返回递归值就行了。但对于情况 1,我们还需要将左右子区间的答案进行合并,也就是再执行一次 push_up 的操作。这样做的原因是我们跳了区间,且答案的分布不在同一个结构体。

代码

#include<bits/stdc++.h>
using namespace std;
#define re register
#define il inline
#define int long long

const int N=1e6+10,inf=1e9,M=1e6+10;
char ch[3][N];
int n=2,m,q;
struct node{
	int l,r,w1,w2,w3,w4;
	//w1:左上-右上 
	//w2:左上-右下
	//w3:左下-右上
	//w4:左下-右下 
}tr[M];

il int work(int ax,int ay,int bx,int by){
	char a=ch[ax][ay],b=ch[bx][by];
	if(a=='X'||b=='X') return inf;
	return 1;
}
il void up(int now){
	tr[now].w1=min(tr[now<<1].w1+tr[now<<1|1].w1+work(1,tr[now<<1].r,1,tr[now<<1|1].l),tr[now<<1].w2+tr[now<<1|1].w3+work(2,tr[now<<1].r,2,tr[now<<1|1].l));
	tr[now].w2=min(tr[now<<1].w1+tr[now<<1|1].w2+work(1,tr[now<<1].r,1,tr[now<<1|1].l),tr[now<<1].w2+tr[now<<1|1].w4+work(2,tr[now<<1].r,2,tr[now<<1|1].l));
	tr[now].w3=min(tr[now<<1].w4+tr[now<<1|1].w3+work(2,tr[now<<1].r,2,tr[now<<1|1].l),tr[now<<1].w3+tr[now<<1|1].w1+work(1,tr[now<<1].r,1,tr[now<<1|1].l));
	tr[now].w4=min(tr[now<<1].w4+tr[now<<1|1].w4+work(2,tr[now<<1].r,2,tr[now<<1|1].l),tr[now<<1].w3+tr[now<<1|1].w2+work(1,tr[now<<1].r,1,tr[now<<1|1].l));
	return ;
}
il node get(node a,node b,int l,int r){
	return 
	{	0,0,
		min(a.w1+b.w1+work(1,l,1,r),a.w2+b.w3+work(2,l,2,r)),
		min(a.w1+b.w2+work(1,l,1,r),a.w2+b.w4+work(2,l,2,r)),
		min(a.w4+b.w3+work(2,l,2,r),a.w3+b.w1+work(1,l,1,r)),
		min(a.w4+b.w4+work(2,l,2,r),a.w3+b.w2+work(1,l,1,r))
	};
}
il void build(int now,int l,int r){
	tr[now].l=l,tr[now].r=r;
	if(l==r){
		tr[now].w2=work(1,l,2,l),tr[now].w3=work(2,l,1,l);
		return ;
	}
	int mid=l+r>>1;
	build(now<<1,l,mid),build(now<<1|1,mid+1,r);
	up(now);
	return ; 
}
il node query(int now,int l,int r){
	if(tr[now].l>=l&&tr[now].r<=r)
		return {0,0,tr[now].w1,tr[now].w2,tr[now].w3,tr[now].w4};
	int mid=tr[now].l+tr[now].r>>1;
	int ans=inf;
	if(l<=mid&&mid<r)//两个区间都包含 
		return get(query(now<<1,l,r),query(now<<1|1,l,r),mid,mid+1);
	else if(l<=mid)//只在左区间
		return query(now<<1,l,r); 
	else if(mid<r)//只在右区间
		return query(now<<1|1,l,r); 
	return {-1,-1,-1,-1,-1,-1};
}

il void solve(){
	scanf("%lld%lld",&m,&q);
	for(re int i=1;i<=n;++i)
		for(re int j=1;j<=m;++j)
			cin>>ch[i][j];
	build(1,1,m);
	for(re int i=1,x,y;i<=q;++i){
		scanf("%lld%lld",&x,&y);
		int a=1,b=1;
		if(x>m) a=2,x-=m;
		if(y>m) b=2,y-=m;
		if(x>y) swap(x,y),swap(a,b);
		node now=query(1,x,y);
		int ans=0;
		if(a==1&&b==1) ans=now.w1;
		else if(a==1&&b==2) ans=now.w2;
		else if(a==2&&b==1) ans=now.w3;
		else ans=now.w4;
		if(ans>=inf) cout<<"-1\n";
		else cout<<ans<<"\n";
	}
	return ;
}

signed main(){
	solve();
	return 0;
}
posted @   harmis_yz  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示