题目大意:一维坐标系上有 $N$ 个城市,每个城市的海拔互不相同,定义两城市间的距离为它们的海拔之差,如果存在两个城市与当前城市的海拔之差相同,则规定海拔较小的城市更近。小 $A$ 与小 $B$ 计划选择一个城市 $S$ 作为起点,然后一直向东行驶,第一天小 $A$ 开车,之后每天轮换一次。每一天他们都会从当前城市到达下一个目的地。小 $B$ 总是沿着前进方向选择一个最近的城市作为目的地,而小 $A$ 总是沿着前进方向选择第二近的城市作为目的地。如果其中任何一人无法按照自己的原则选择目的城市,或者到达目的地会使行驶的总距离超出 $X$ 公里,他们就会结束旅行。请你回答下面两个问题:
- 对于给定的 $X_0$,从哪一个城市出发,小 $A$ 开车行驶的路程总数与小 $B$ 行驶的路程总数的比值最小?
- 对于给定的 $S_i,X_i$,小 $A$ 行驶的里程总数和小 $B$ 行驶的里程总数各是多少? ($i=1 \cdots M$)
$1 \leq N,M \leq 100000$
预处理距每个点第一第二近的城市可以用平衡树,考虑到只是查询前驱和后继,可以使用 $multiset$。
发现每 $2^i\ (i>0)$ 天结束后,都是再由小 $A$ 出发,然后就可以愉快地倍增了٩(๑>◡<๑)۶
#include <cstdio>
#include <algorithm>
#include <set>
const int N = 100005, INF = 0x3f3f3f3f;
int h[N], f[N][20], tmp1[N], tmp2[N], a[N][20], b[N][20];
struct Node1 {
int h, id;
bool operator < (const Node1 &rhs) const {
return h < rhs.h;
}
};
struct Node2 {
int h1, h2, id;
bool operator < (const Node2 &rhs) const {
if (h1 == rhs.h1) return h2 < rhs.h2;
return h1 < rhs.h1;
}
} c[10];
std::multiset<Node1> S;
std::multiset<Node1>::iterator it;
int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x * f;
}
int abs(int x) {
return x > 0 ? x : -x;
}
void jump(int s, int x, int &aa, int &bb) {
for (int i = 17; i >= 1; --i) {
if (!a[s][i] || !b[s][i] || aa + bb + a[s][i] + b[s][i] > x) continue;
aa += a[s][i], bb += b[s][i], s = f[s][i];
}
if (a[s][0] && aa + bb + a[s][0] <= x) aa += a[s][0];
}
int main() {
int n = read();
for (int i = 1; i <= n; ++i) h[i] = read();
S.insert((Node1){-INF, 0}), S.insert((Node1){INF, 0});
for (int i = n; i >= 1; --i) {
it = S.lower_bound((Node1){h[i], i});
c[0].id = 0;
if (it->h < INF) {
c[++c[0].id] = (Node2){abs(h[i]-it->h), it->h, it->id};
if ((++it)->h < INF)
c[++c[0].id] = (Node2){abs(h[i]-it->h), it->h, it->id};
--it;
}
if ((--it)->h > -INF) {
c[++c[0].id] = (Node2){abs(h[i]-it->h), it->h, it->id};
if ((--it)->h > -INF)
c[++c[0].id] = (Node2){abs(h[i]-it->h), it->h, it->id};
}
std::sort(c + 1, c + c[0].id + 1);
if (c[0].id >= 1) tmp1[i] = c[1].id, tmp2[i] = c[1].h1;
if (c[0].id >= 2) f[i][0] = c[2].id, a[i][0] = a[i][1] = c[2].h1;
S.insert((Node1){h[i], i});
}
for (int i = 1; i <= n; ++i)
f[i][1] = tmp1[f[i][0]], b[i][1] = tmp2[f[i][0]];
for (int j = 2; j <= 17; ++j)
for (int i = 1; i <= n; ++i) {
f[i][j] = f[f[i][j-1]][j-1];
if (f[i][j]) {
a[i][j] = a[i][j-1] + a[f[i][j-1]][j-1];
b[i][j] = b[i][j-1] + b[f[i][j-1]][j-1];
}
}
int x = read(), s = 0, aa, bb;
double ratio = INF;
for (int i = 1; i <= n; ++i) {
aa = bb = 0;
jump(i, x, aa, bb);
if (bb && aa * 1.0 / bb < ratio)
ratio = aa * 1.0 / bb, s = i;
}
printf("%d\n", s);
int m = read();
while (m--) {
s = read(), x = read();
aa = bb = 0;
jump(s, x, aa, bb);
printf("%d %d\n", aa, bb);
}
return 0;
}