P1081 [NOIP 2012 提高组] 开车旅行

[ P1081 [NOIP 2012 提高组] 开车旅行]([P1081 [NOIP 2012 提高组] 开车旅行 \(O(nlgn)\)

预处理

预处理出小A和小B在每个城市下次到达的城市,即距离每个点的最近点和次近点

最近点和次近点只可能在前驱、前驱的前驱、后继、后继的后继中

数轴上找前驱和后继

排序、动态维护数轴

求前驱、求后继、删除。链表 \(O(1)\) 维护

    for(int i=1;i<=n;i++){
		cin>>a[i].h;
		a[i].id=i;
		nxt[i]=i+1;
		pre[i]=i-1;
	}
  	nxt[0]=nxt[n]=0;
  	sort(a+1,a+1+n,cmp);
  	for(int i=1;i<=n;i++)
  		b[a[i].id]=i;
  	//A数组,B数组:下次到达城市的序号,AW,BW:行驶的路程
  	for(int i=1;i<=n;i++){
  		int u=pre[b[i]],v=nxt[b[i]];
  		if(!u&&!v) continue;
  		if(v&&(!u||abs(a[u].h-a[b[i]].h)>abs(a[v].h-a[b[i]].h))){
  			B[i]=a[v].id;
  			BW[i]=abs(a[v].h-a[b[i]].h);
  		}
  		else if(u&&(!v||abs(a[v].h-a[b[i]].h)>abs(a[u].h-a[b[i]].h))){
  			B[i]=a[u].id;
  			BW[i]=abs(a[u].h-a[b[i]].h);
  		}
  		else if(u&&v){
  			if(abs(a[v].h-a[b[i]].h)<abs(a[u].h-a[b[i]].h)){
  				B[i]=a[v].id;
  				BW[i]=abs(a[v].h-a[b[i]].h);
  			}
  			else{
  				B[i]=a[u].id;
  				BW[i]=abs(a[u].h-a[b[i]].h);
  			}
  		}
  		if(u) nxt[u]=v;
  		if(v) pre[v]=u;
  	}
  	for(int i=1;i<=n;i++){
  		nxt[i]=i+1;
  		pre[i]=i-1;
  	}
  	pre[0]=nxt[n]=nxt[0]=0;
  	for(int i=1;i<=n;i++){
  		int u=pre[b[i]],v=nxt[b[i]];
  		if(!u&&!v) continue;
  		if((!u&&nxt[v])||(u&&nxt[v]&&abs(a[u].h-a[b[i]].h)>abs(a[b[i]].h-a[nxt[v]].h))){
  			A[i]=a[nxt[v]].id;
  			AW[i]=abs(a[b[i]].h-a[nxt[v]].h);
  		}
  		else if((!v&&pre[u])||(v&&pre[u]&&abs(a[v].h-a[b[i]].h)>=abs(a[pre[u]].h-a[b[i]].h))){
  			A[i]=a[pre[u]].id;
  			AW[i]=abs(a[pre[u]].h-a[b[i]].h);
  		}
  		else if(u&&v){
  			//距离相等时选前驱对应题目中"距离相同则选海拔较低"的规则(前驱海拔更低)
  			if(abs(a[v].h-a[b[i]].h)<abs(a[u].h-a[b[i]].h)){
  				A[i]=a[u].id;
  				AW[i]=abs(a[u].h-a[b[i]].h);
  			}
  			else{
  				A[i]=a[v].id;
  				AW[i]=abs(a[v].h-a[b[i]].h);
  			}
  		}
  		if(u) nxt[u]=v;
  		if(v) pre[v]=u;
  	}

[!NOTE]

注意判定当前元素的前驱和后继不存在的情况

倍增优化移动

\(\large s[i][j]\) 表示从\(i\)号节点小\(A\)和小\(B\)各走\(2^j\)步到达的节点

\(SW[i][j]\) 表示从\(i\)号节点小\(A\)和小\(B\)各走\(2^j\)步的权值

\(SA[i][j]\) 表示从\(i\)号节点小\(A\)\(2^j\)步的权值

\(SB[i][j]\) 表示从\(i\)号节点小\(B\)\(2^j\)步的权值

    for(int i=1;i<=n;i++){
		if(A[i]&&B[A[i]]){
			s[i][0]=B[A[i]];
			SW[i][0]=min(INF,AW[i]+BW[A[i]]);
			SB[i][0]=min(INF,BW[A[i]]);
			SA[i][0]=min(INF,AW[i]);
		}
	}
	for(int j=1;j<20;j++){
		for(int i=1;i<=n;i++){
			if(s[i][j-1]&&s[s[i][j-1]][j-1]){
				s[i][j]=s[s[i][j-1]][j-1];
				SW[i][j]=min(INF,SW[i][j-1]+SW[s[i][j-1]][j-1]);
				SB[i][j]=min(INF,SB[i][j-1]+SB[s[i][j-1]][j-1]);
				SA[i][j]=min(INF,SA[i][j-1]+SA[s[i][j-1]][j-1]);
			}
		}
	}

1、到达极限后,再尝试一下小 A 能不能再走,比较大小

2、最大值不是 \({10}^9\)

cin>>x;
int ans1=INF+1,ans2=0,t=0;//t记录编号 
for(int i=1;i<=n;i++){
	int sA=0,sB=0,S=0;
	int u=i;
	for(int j=19;j>=0;j--){
		if(s[u][j]){
			if((ll)S+SW[u][j]<=x){
				S+=SW[u][j];
				sA+=SA[u][j];
				sB+=SB[u][j];
				u=s[u][j];
			}
		}
	}
	if(A[u]&&S+AW[u]<=x) sA+=AW[u];
	if((ll)ans1*sB>(ll)sA*ans2){
		ans1=sA;
		ans2=sB;
		t=i;
	}
}
cout<<t<<'\n';
cin>>m;
while(m--){
	cin>>st>>x;
	int S=0,sA=0,sB=0;
	for(int j=19;j>=0;j--){
		if(s[st][j]){
			if((ll)S+SW[st][j]<=x){
				S+=SW[st][j];
				sA+=SA[st][j];
				sB+=SB[st][j];
				st=s[st][j];
			}
		}
	}
	if(A[st]&&S+AW[st]<=x) sA+=AW[st];
	cout<<sA<<" "<<sB<<'\n';
} 
posted @ 2026-06-07 20:49  Aguanenti  阅读(3)  评论(0)    收藏  举报