*题解:P12700 [KOI 2022 Round 2] 停车场

原题链接

解析

\(f_i\) 表示取完了编号为 \(a_i\) 的车,最后取出的是第 \(i\) 个格子上的车所需的最少操作次数。枚举上一层终点 \(j\) 进行转移:

\[f_i=\min(f_j + \operatorname{dis}(pre_i,j) + \operatorname{ACW}(pre_i,i),f_j + \operatorname{dis}(nxt_i,j) + \operatorname{CW}(nxt_i,i)) \]

其中 \(\operatorname{dis}(i,j)\) 表示从第 \(i\) 个格子走到第 \(j\) 个格子所需的最少步数,\(\operatorname{CW}(i,j)\)\(\operatorname{ACW}(i,j)\) 分别表示从 \(i\) 顺、逆时针走到 \(j\) 的步数,\(pre_i,nxt_i\) 分别表示在 \(i\) 之前逆时针和 \(i\) 之后顺时针第一个编号为 \(a_i\) 的位置。

这样直接转移复杂度是 \(O(n)\) 的,考虑优化,以 \(f_j + \operatorname{dis}(pre_i,j) + \operatorname{ACW}(pre_i,i)\) 为例,先把和 \(j\) 有关的项提取出来:

\[f_j + \operatorname{dis}(pre_i,j) \]

拆开 \(\operatorname{dis}\) 变为:

\[f_j + \operatorname{min}(\lvert pre_i-j\rvert,n-\lvert pre_i-j\rvert) \]

钦定 \(pre_i > j\) 拆绝对值:

\[f_j + \operatorname{min}(pre_i-j,n-pre_i+j) \]

只考虑跟 \(j\) 有关的项,求出 \(f_j - j\)\(f_j + j\) 的前缀最小值再加上二分就可以做到 \(O(\log n)\) 转移。\(pre_i \le j\) 的情况同理,详见代码。

代码

时间复杂度 \(O(n\log n)\)。当然你也可以用双指针替代二分从而省下转移过程中的 \(\log\)

#include <bits/stdc++.h>
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
#define mid ((l + r) >> 1)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 1e6 + 5,M = 100 + 5,mod = 998244353;
int a[N],pre[N],nxt[N];
ll f[N],premn1[N],premn2[N],sufmn1[N],sufmn2[N];
vector<int> pos[N];
int n;
int CW(int a,int b){
	if(b >= a) return b - a;
	return n - (a - b);
}
int ACW(int a,int b){
	if(a >= b) return a - b;
	return n - (b - a);
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	memset(f,127,sizeof(f));
	memset(premn1,127,sizeof(premn1));
	memset(premn2,127,sizeof(premn2));
	memset(sufmn1,127,sizeof(sufmn1));
	memset(sufmn2,127,sizeof(sufmn2));
	cin>>n;
	vector<int> v;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		v.push_back(a[i]);
	}
	sort(v.begin(),v.end());
	v.erase(unique(v.begin(),v.end()),v.end());
	for(int i=1;i<=n;i++){
		a[i] = lower_bound(v.begin(),v.end(),a[i]) - v.begin() + 1;
		pos[a[i]].push_back(i);
	}
	for(int i=1;i<=v.size();i++){
		for(int j=0;j<pos[i].size();j++){
			if(j)
				pre[pos[i][j]] = pos[i][j - 1];
			if(j + 1 < pos[i].size())
				nxt[pos[i][j]] = pos[i][j + 1];
		}
		pre[pos[i][0]] = pos[i].back();
		nxt[pos[i].back()] = pos[i][0];
	}
	pos[0].push_back(0);
	f[0] = 0;
	premn1[0] = sufmn1[0] = 1;
	premn2[0] = sufmn2[0] = -1;
	ll res = 9e18;
	for(int i=1;i<=v.size();i++){
		for(int j : pos[i]){
			auto it = lower_bound(pos[i - 1].begin(),pos[i - 1].end(),pre[j]);//pre[j] < k
			int k = pos[i - 1].back();
			//f[k] + min(k - pre[j],n - k + pre[j])
			if(it != pos[i - 1].end()){
				k = *it;
				f[j] = min(f[j],min(sufmn1[k] - pre[j],n + sufmn2[k] + pre[j]) + ACW(pre[j],j));
			}
			if(k > pre[j]) k = pre[k];
			if(k < pre[j]){
				//pre[j] > k
				//f[k] + min(pre[j] - k,n - pre[j] + k)
				f[j] = min(f[j],min(premn2[k] + pre[j],n + premn1[k] - pre[j]) + ACW(pre[j],j));
			}
			it = lower_bound(pos[i - 1].begin(),pos[i - 1].end(),nxt[j]);
			k = pos[i - 1].back();
			if(it != pos[i - 1].end()){
				k = *it; 
				f[j] = min(f[j],min(sufmn1[k] - nxt[j],n + sufmn2[k] + nxt[j]) + CW(nxt[j],j));
			}
			if(k > nxt[j]) k = pre[k];
			if(k < nxt[j]){
				f[j] = min(f[j],min(premn2[k] + nxt[j],n + premn1[k] - nxt[j]) + CW(nxt[j],j));
			}
			premn1[j] = f[j] + j,premn2[j] = f[j] - j;
			if(pre[j] < j){
				premn1[j] = min(premn1[j],premn1[pre[j]]);
				premn2[j] = min(premn2[j],premn2[pre[j]]);
			}
			if(i == v.size()){
				res = min(res,f[j]);
			}
		}
		for(int j=pos[i].size() - 1;j>=0;j--){
			int t = pos[i][j];
			sufmn1[t] = f[t] + t,sufmn2[t] = f[t] - t;
			if(j < pos[i].size() - 1){
				sufmn1[t] = min(sufmn1[t],sufmn1[nxt[t]]);
				sufmn2[t] = min(sufmn2[t],sufmn2[nxt[t]]);
			}
		}
	}
	cout<<res;
	return 0;
}
posted @ 2025-10-22 12:03  yutar  阅读(6)  评论(0)    收藏  举报