「NOIP2012」开车旅行
知识点:模拟,倍增
简述
懒得简述的题面。
分析
先考虑暴力模拟。先预处理出从每个城市出发,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;
}
作者@Luckyblock,转载请声明出处。