专栏文章

【题解】 比太郎之旅

题解参与者 4已保存评论 3

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
2 条
当前快照
1 份
快照标识符
@mindgi3b
此快照首次捕获于
2025/12/02 00:36
3 个月前
此快照最后确认于
2025/12/02 00:36
3 个月前
查看原文

背景

出到了校内模拟赛中,写出目前没有的 O(nlogn+q)O(n \log n + q) 的做法,获得了本题最优解。

题意

你有 nn 个点,第 ii 个点位于坐标 aia_i,现在有一个人从 xx 坐标出发,每次选择目前的离他最近且没有到达过的点,问他最后遍历完所有点时坐过的距离和,多次询问。

思路

我们先考虑从每个点出发的答案,记作 ansians_i
首先 ans1=ana1ans_1 = a_n - a_1,含义是从第一个点出发肯定直接从左往右走就行了。
之后我们考虑 i=2i = 2 的情况,我们考虑从二号点出发的答案一定是先往右走一段,如果走到 nn 了,走回来就行了,如果没有走到 nn,那么就要先回到 11 号点,然后走到 nn 号点,接下来我们只需要解决从每个点出发往右走一直能够走到哪里就行了。
我们考虑如何计算往右能走到哪? 把这个东西记作 rir_i
对于相邻的两个点 i,i+1i, i + 1 之间的连边,我们计算 ii 在什么时候会被当作最靠右的边,这种贡献的点就是一个从 ii 往前的后缀,证明:我们写一下表达式,如果 jj 能到达 i1i - 1 并且在到达 ii 时往回走,那么就有表达式:aiajai+1aia_i - a_j \le a_{i + 1} - a_i 我们移一下项,就有:ajai+aiai+1a_j \ge a_i + a_i - a_{i + 1} 这个是好二分的,那么我们就可以通过写一个维护区间取 min\min ,修改完后单点查询的数据结构,笔者这里写的是 multiset
现在我们已经解决了从 22 开始的答案,现在我们尝试往 i>2i > 2 推广,如图:
我们走的路线一定可以刻画成上面那种模式,考虑从 ii 开始的答案一定是从 ii 走到 rir_i,在往回走到 xx,之后就转化为从 xx 开始的长度。
那么我们只需要能求出来 xx 就能通过递推的方式转移出来答案。
假如说我们现在确定从 ii 走到 rir_i,那么我们如何求出 xx 呢?
注意到 xx 是满足以下条件中最大的那个:
  • x<ix < i
  • ariax<axax1a_{r_i} - a_x < a_{x} - a_{x - 1}
第一个限制是好做的,现在我们考虑如何满足第二个限制。
我们变一下型,可以转化为:ari<ax+axax1a_{r_i} < a_x + a_x - a_{x - 1},我们发现右边对于相同的 xx 而言是个定值,且 满足条件的 rir_i 一定是一段从 xx 开始的前缀,又可以二分了,我们记 kxk_x 表示 rir_i 最大能去到哪里。
那么上述的第两个条件就转化为:
  • ri<kxr_i < k_x
这个我们再变一下型:
  • ri<maxj=xikjr_i < \max_{j = x}^{i} k_j
由于我们要求的是最小的 xx,所以我们只需要用个能够支持静态查询区间最大值的数据结构加上二分就可以了,笔者这里写的是 st表。
我们已经求出来 xx 了,那就有转移:
ansi=ansx+ariai+ariaxans_i = ans_x + a_{r_i} - a_i + a_{r_i} - a_x
最后他让求的是从任意坐标开始的答案,我们找到离我们询问最近的点就行了。
分析一波复杂度,求 ri,kir_i,k_iO(nlogn)O(n \log n) 的,求 xxO(logn)O(\log n) 的,一次询问是 O(1)O(1) 返回,总复杂度为 O(nlogn+q)O(n \log n + q)

代码

CPP
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned long long
#define double long double
#define Air
namespace io{inline int read(){int f=1,t=0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}while(ch>='0'&&ch<='9'){t=t*10+ch-'0';ch=getchar();}return t*f;}inline void write(int x){if(x<0){putchar('-');x=-x;}if(x>=10){write(x/10);}putchar(x%10+'0');}}
using namespace io;
int n, q;
const int N = 2e5 + 10;
int a[N];
int rid[N];
int tid[N];
int ans[N];
vector<int>stt[N], edd[N];
int st[21][N];
int pre[N];
int st_ask(int x, int y){
	int step = pre[y - x + 1];
	return max(st[step][x], st[step][y - (1ll << (step)) + 1]);
}
signed main() {
#ifndef Air
	freopen("bitaro.in","r",stdin);
	freopen("bitaro.out","w",stdout);
#endif
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	n = read();
	rid[1] = n;
	pre[1] = 0;
	for(int i = 2; i <= n; i++){
		pre[i] = pre[i / 2] + 1;
	}
	for(int i = 1; i <= n; i++){
		a[i] = read(); 
	}
	for(int i = 1; i <= n; i++){
		if(i >= 2){	
			rid[i] = lower_bound(a + 1, a + 1 + n, a[i] + a[i] - a[i - 1]) - a - 1;
		}
		st[0][i] = rid[i];
	}
	for(int i = 1; i <= 20; i ++){
		for(int j = 1; j + (1ll << (i)) - 1 <= n; j++){
			st[i][j] = max(st[i - 1][j], st[i - 1][j + (1ll << (i - 1))]);
		}
	}
	for(int i = 2; i < n; i++){
		int dis = a[i + 1] - a[i];
		int tmp = lower_bound(a + 1, a + i, a[i] - dis) - a;
		if(tmp == i) continue;
		stt[tmp + 1].push_back(i);
		edd[i + 1].push_back(i);
	}
	ans[1] = a[n] - a[1];
	multiset<int>mt;
	mt.insert(n);
	for(int i = 1; i <= n; i++){
		for(auto y: stt[i]){
			mt.insert(y);
		}
		for(auto y: edd[i]){
			mt.erase(mt.find(y));
		}
		tid[i] = *mt.begin();
	}
	for(int i = 2; i <= n; i++){
		if(tid[i] == n) {
			ans[i] = a[n] - a[i] + a[n] - a[1];
			continue;
		}
		int l = 1, r = i - 1;
		while(l + 1 < r){
			int mid = (l + r) >> 1;
			if(st_ask(mid, i - 1) > tid[i]){
				l = mid;
			}
			else{
				r = mid;
			}
		}
		int pos = 0;
		for(int j = r; j >= l; j--){
			if(st_ask(j, i - 1) > tid[i]){
				pos = j;
				break;
			}
		}
		ans[i] = ans[pos] + a[tid[i]] - a[i] + a[tid[i]] - a[pos];
	}
	q = read();
	while(q--){
		int x = read();
 		if(x >= a[n]){
			cout << ans[n] + (x - a[n])<< '\n';
			continue;
		}
		if(x <= a[1]){
			cout << ans[1] + (a[1] - x) << '\n';
			continue;
		}
		int tmp = lower_bound(a + 1, a + 1 + n, x) - a;
		if(a[tmp] - x < x - a[tmp - 1]){
			cout << ans[tmp] + a[tmp] - x << '\n';
		}
		else{
			cout << ans[tmp - 1] + (x - a[tmp - 1]) << '\n';
		}
	}
	return 0;
}

评论

3 条评论,欢迎与作者交流。

正在加载评论...