「NOIP2012」开车旅行

知识点:模拟,倍增

链接:LojLuogu

简述

懒得简述的题面。

分析

先考虑暴力模拟。先预处理出从每个城市出发,A/B 开车到达的下一个城市。预处理时可以考虑倒序枚举所有城市,维护一棵以海拔为权值的平衡树,将当前枚举的城市插入平衡树,查询前驱、前驱的前驱、后继、后继的后继,在这四个城市中找距离的最小值和次小值即可。注意先插入两个极小值和两个极大值。之后对于第一、二问,模拟每天的行程直至无法前进即可。

这样每次查询的复杂度是 \(O(n)\) 的,考虑优化,倍增预处理从每个城市出发,A/B 先开车,经过 \(2^i\) 天的行程信息。具体地,设:\(f_{i,j,0/1}\) 表示 A/B 先开车,从城市 \(j\) 出发,经过 \(2^i\) 天后到达的城市,\(\operatorname{disa}_{i,j,0/1}\)\(\operatorname{disb}_{i,j,0/1}\) 表示表示 A/B 先开车,从城市 \(j\) 出发,经过 \(2^i\) 天后 A/B 的行程。首先根据暴力中预处理的信息初始化 \(f_{0,i,0/1}, \operatorname{disa}_{0,i,0/1}, \operatorname{disb}_{0,i,0/1}\),然后进行倍增,有:

  • \(i=1\) 时,前半段和后半段路程开车人不同。设前半段开车人为 \(k\),则有:

    \[\begin{aligned} f_{1, j, k} &= f_{0, (f_{0, j, k}), 1-k}\\ \operatorname{disa}_{1, j, k} &= \operatorname{disa}_{0, j, k} + \operatorname{disa}_{0, (f_{0, j, k}), 1-k} \\ \operatorname{disb}_{1, j, k} &= \operatorname{disb}_{0, j, k} + \operatorname{disb}_{0, (f_{0, j, k}), 1-k} \end{aligned} \]

  • \(i>1\) 时,有:

    \[\begin{aligned} f_{i, j, k} &= f_{i - 1, (f_{i - 1, j, k}), k}\\ \operatorname{disa}_{i, j, k} &= \operatorname{disa}_{i - 1, j, k} + \operatorname{disa}_{i - 1, (f_{i - 1, j, k}), k} \\ \operatorname{disb}_{i, j, k} &= \operatorname{disb}_{i - 1, j, k} + \operatorname{disb}_{i - 1, (f_{i - 1, j, k}), k} \end{aligned} \]

利用倍增预处理的信息加速模拟的过程即可,总复杂度 \(O((n + m)\log n)\) 级别。

set 的迭代器只能进行 ++/-- 操作,注意从 set 中取前驱后继的写法。

代码

//By:Luckyblock
/*
*/
#include <set>
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
const int kN = 1e5 + 10;
const int Inf = 2e9 + 7;
//=============================================================
struct City {
	int id, h;
	friend bool operator < (City fir_, City sec_) {
		return fir_.h < sec_.h;
	}
};
int n, m, nowh, h[kN];
int nexta[kN], nextb[kN];
int f[20][kN][2], disa[20][kN][2], disb[20][kN][2];
int x0, suma, sumb, ans1;
double dans1 = Inf;
//=============================================================
inline int read() {
	int f = 1, w = 0; char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
	for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
	return f * w;
}
bool cmp(City fir_, City sec_) {
	if (abs(fir_.h - nowh) != abs(sec_.h - nowh)) {
		return abs(fir_.h - nowh) < abs(sec_.h - nowh);
	}
	return fir_.h < sec_.h;
}
void Init() {
	n = read();
	for (int i = 1; i <= n; ++ i) h[i] = read();
	
	std::multiset <City> s;
	h[0] = Inf, h[n + 1] = -Inf;
	s.insert((City) {0, Inf}), s.insert((City) {n + 1, -Inf}); //��Сֵ 
	s.insert((City) {0, Inf}), s.insert((City) {n + 1, -Inf});
	for (int i = n; i; -- i) {
		s.insert((City) {i, h[i]}); nowh = h[i];
		std::set<City>::iterator p = s.lower_bound((City) {i, h[i]});
		std::vector <City> c;
		c.push_back(*(-- p)); c.push_back(*(-- p)); ++ p, ++ p; 
		c.push_back(*(++ p)); c.push_back(*(++ p));
		std::sort(c.begin(), c.end(), cmp);
		nextb[i] = c[0].id, nexta[i] = c[1].id;	
	}
	
	for (int i = 1; i <= n; ++ i) {
		f[0][i][0] = nexta[i], f[0][i][1] = nextb[i];
		disa[0][i][0] = abs(h[nexta[i]] - h[i]);
		disb[0][i][1] = abs(h[nextb[i]] - h[i]);
	}
	for (int i = 1; i <= 18; ++ i) {
		for (int j = 1; j <= n; ++ j) {
			for (int k = 0; k < 2; ++ k) {
				int mid = f[i - 1][j][k];
				if (i == 1) {
					f[1][j][k] = f[0][mid][k ^ 1];
					disa[1][j][k] = disa[0][j][k] + disa[0][mid][k ^ 1];
					disb[1][j][k] = disb[0][j][k] + disb[0][mid][k ^ 1];
				} else {
					f[i][j][k] = f[i - 1][mid][k];
					disa[i][j][k] = disa[i - 1][j][k] + disa[i - 1][mid][k];
					disb[i][j][k] = disb[i - 1][j][k] + disb[i - 1][mid][k];
				}
			}
		}
	}
}
void Calc(int s_, int x_) {
	suma = sumb = 0;
	for (int i = 18; i >= 0; -- i) {
		if (f[i][s_][0] && 
				suma + sumb + disa[i][s_][0] + disb[i][s_][0] <= x_) {
			suma += disa[i][s_][0];
			sumb += disb[i][s_][0];
			s_ = f[i][s_][0];			
		}
	}
}
//=============================================================
int main() {
//	freopen("1.txt", "r", stdin);
	Init();
	x0 = read();
	for (int i = 1; i <= n; ++ i) {
		Calc(i, x0);
		if (1.0 * suma / sumb < dans1) {
			dans1 = 1.0 * suma / sumb, ans1 = i;
		} else if (1.0 * suma / sumb == dans1 && h[ans1] < h[i]){
			ans1 = i;
		}
	}
	printf("%d\n", ans1);
	
	m = read();	
	while (m --) {
		int s = read(), x = read();
		Calc(s, x);
		printf("%d %d\n", suma, sumb);
	}
	return 0;
}
posted @ 2023-01-17 18:59  Luckyblock  阅读(71)  评论(0编辑  收藏  举报