题解:洛谷 P10803([CEOI 2024] 文本编辑器)

1. Solution

比较简单的题。
首先你会发现一条(不太可能)的道路长度就是这两点之间的曼哈顿距离,然后考虑什么时候这个距离会发生变化,很显然,当你在上下移动的时候因为不可抗力被跳到了行尾,或者在行尾按下左键到达下一行行首。
所以很自然的可以得到结论:最优路径必然只有三种情况,从起点直接走到终点,从起点先到某一行行尾再到终点,从起点先到某一行行首再到终点。
首先考虑第一种情况:
设这两个位置分别为 \((x_1,y_1)\)\((x_2,y_2)\)\(mi\) 为起点和终点之间行的 \(l\) 的最小值。
如果 \(mi+1\ge max(y_1,y_2)\),那么步数就是 \(|x_1-x_2|+|y_1-y_2|\)
如果 \(y_2\le mi+1<y_1\),那么步数就是 \(|x_1-x_2|+|mi+1-y_2|\)
如果 \(y_1\le mi+1<y_2\),那么步数就是 \(|x_1-x_2|+|y_1-y_2|\)
如果 \(mi+1\le min(y_1,y_2)\),那么不是就是 \(|x_1-x_2|+|mi+1-y_2|\)
然后考虑第二种和第三种情况。
此时需要求出起点到每一行行尾和行首的最小步数。应该不难求出 \(up_i\) 表示从第 \(i\) 行行尾到 \(i-1\) 行行尾的最小步数,\(down_i\) 表示从第 \(i\) 行行尾到 \(i+1\) 行行尾的最小步数。
然后大力讨论一下从起点到 \(sl,sl-1,sl\) 的行尾最小步数,其他的位置就用第一种情况的算法作为初始值,最后跑 Dijkstra 即可,具体转移可以看代码。

2. Code

/*by ChenMuJiu*/
/*略去缺省源与快读快写*/
#include<bits/stdc++.h> 
#define int long long
#define pii pair<int,int>
#define Name 838412064
#define raed(x) read(x)
#define Nxt puts("")
#define Spa putchar(32)
#define Pline puts("------------------------------")
namespace FastIO{
	int write_top,read_f,read_x;
	char read_char;
	int write_st[20];
	inline int read(int &a){
		read_char=getchar();
		read_f=1;
		a=0;
		while(!isdigit(read_char)){
			if(read_char=='-')read_f=-1;
			read_char=getchar();
		}
		while(isdigit(read_char)){
			a=(a<<1)+(a<<3)+(read_char^48);
			read_char=getchar();
		}
		return a=a*read_f;
	}
	inline int read(){
		read_char=getchar();
		read_f=1;
		read_x=0;
		while(!isdigit(read_char)){
			if(read_char=='-')read_f=-1;
			read_char=getchar();
		}
		while(isdigit(read_char)){
			read_x=(read_x<<1)+(read_x<<3)+(read_char^48);
			read_char=getchar();
		}
		return read_x*read_f;
	}
	inline void write(long long x){
		if(x<0)putchar('-'),x=-x;
		write_top=0;
		do{
		   write_st[++write_top]=x%10;
		   x/=10;
		}while(x);
		while(write_top)putchar(write_st[write_top--]+'0');
		return ;
	}
	inline void tomax(int &a,int b){
		if(a<b)a=b;
		return ;
	}
	inline void tomin(long long &a,long long b){
		if(a>b)a=b;
		return ;
	}
}
using namespace FastIO;
using namespace std;
const int N=1e6+5;
int n;
int sl,sc,el,ec;
int l[N];
int up[N];
int down[N];
int st[25][N];
int dis[N*2];
bool vis[N*2];
/*
1~n 表示到第 i 行行尾  
n+1~2*n 表示到第 i-n 行行首   
*/
int query(int l,int r){
	int j=__lg(r-l+1);
	return min(st[j][l],st[j][r-(1<<j)+1]);
}
int dist(int sx,int sy,int tx,int ty){
	int mi=query(min(sx,tx),max(sx,tx))+1;
	if(sy<=ty){
		if(mi<sy)return abs(tx-sx)+ty-mi;
		return abs(tx-sx)+ty-sy;
	}else{
		if(mi<ty)return abs(tx-sx)+ty-mi;
		return abs(tx-sx)+min(mi,sy)-ty;
	}
}
void Dijkstra(){
	struct Node{
		int dis,u;
		bool operator >(const Node &T)const{
			return dis>T.dis;
		}
	};
	priority_queue<Node,vector<Node>,greater<Node>>q;
	memset(dis,0x3f,sizeof(dis));
	dis[sl]=l[sl]+1-sc;
	q.push({dis[sl],sl});
	if(sl>1){
		if(sc>=l[sl-1]+1)dis[sl-1]=1; 
		else dis[sl-1]=min(sc,2+l[sl-1]-sc);
		q.push({dis[sl-1],sl-1});
	}
	if(sl<n){
		if(sc>=l[sl+1]+1)dis[sl+1]=1;
		else dis[sl+1]=min(l[sl]-sc+l[sl+1]+2,2+l[sl+1]-sc);
		q.push({dis[sl+1],sl+1});
	}
	for(int i=sl-2;i>=1;i--){
		dis[i]=dist(sl,sc,i,l[i]+1);
		q.push({dis[i],i});
	}
	for(int i=sl+2,now=min(sc,l[sl+1]+1);i<=n;i++){
		dis[i]=dist(sl,sc,i,l[i]+1);
		q.push({dis[i],i});
	}
	for(int i=sl,now=sc;i>=1;i--){
		dis[i+n]=dist(sl,sc,i,1);
		q.push({dis[i+n],i+n});
	}
	for(int i=sl,now=sc;i<=n;i++){
		dis[i+n]=dist(sl,sc,i,1);
		q.push({dis[i+n],i+n});
	}
	while(!q.empty()){
		int u=q.top().u;
		q.pop();
		if(vis[u])continue;
		vis[u]=1;
		if(u<=n){
			if(u>1&&dis[u-1]>dis[u]+up[u]){//u 尾->u-1 尾 
				dis[u-1]=dis[u]+up[u];
				q.push({dis[u-1],u-1});
			}
			if(u<n&&dis[u+1]>dis[u]+down[u]){//u 尾->u+1 尾  
				dis[u+1]=dis[u]+down[u];
				q.push({dis[u+1],u+1});
			}
			if(u<n&&dis[u+1+n]>dis[u]+1){//u 尾->u+1 首  
				dis[u+1+n]=dis[u]+1;
				q.push({dis[u+1+n],u+1+n});
			}
			if(dis[u+n]>dis[u]+l[u]){//u 尾->u 首  
				dis[u+n]=dis[u]+l[u];
				q.push({dis[u+n],u+n});
			}
		}else{
			int now=u-n;
			if(now>1&&dis[u-1]>dis[u]+1){//now 首->now-1 首 
				dis[u-1]=dis[u]+1;
				q.push({dis[u-1],u-1});
			}
			if(now<n&&dis[u+1]>dis[u]+1){//now 首->now+1 首  
				dis[u+1]=dis[u]+1;
				q.push({dis[u+1],u+1});
			}
			if(now>1&&dis[now-1]>dis[u]+1){//now 首->now-1 尾 
				dis[now-1]=dis[u]+1;
				q.push({dis[now-1],now-1});
			}
			if(dis[now]>dis[u]+l[now]){//now 首->now 尾 
				dis[now]=dis[u]+l[now];
				q.push({dis[now],now});
			} 
		}
	}
}
void solve(){
	for(int i=1;i<=n;i++)
		st[0][i]=l[i];
	for(int j=1;j<=20;j++)
		for(int i=1;i+(1<<j)-1<=n;i++)
			st[j][i]=min(st[j-1][i],st[j-1][i+(1<<j-1)]);
	for(int i=2;i<=n;i++){
		if(l[i]+1>=l[i-1]+1)up[i]=1;
		else up[i]=min(l[i]+1,1+l[i-1]-l[i]);
	}
	for(int i=1;i<n;i++){
		if(l[i]+1>=l[i+1]+1)down[i]=1;
		else down[i]=min(l[i+1]+1,1+l[i+1]-l[i]);
	}
	Dijkstra();
	int ans=dist(sl,sc,el,ec);
	for(int i=1;i<=n;i++){
		tomin(ans,dis[i]+dist(i,l[i]+1,el,ec));
		tomin(ans,dis[i+n]+dist(i,1,el,ec));
	}
	write(ans),Nxt;
}
signed main(){
	read(n);
	read(sl),read(sc);
	read(el),read(ec);
	for(int i=1;i<=n;i++)
		read(l[i]);
	solve();
}
posted @ 2026-02-01 22:09  陈牧九  阅读(3)  评论(0)    收藏  举报