[NOIp2012]开车旅行
Description
Solution
首先可以暴力模拟走的过程即可得70分。
观察暴力效率低下的原因,其一是向前走的时候比较慢,用倍增的思路优化即可。其二是读入时处理的每个点到每个点的距离,这是不必要的。
那么如何改进呢?首先,我们发现对于每个点只需要求出最近点和次近点即可,这里可以轻松的想到排序,但是比较难想到的是利用链表来处理“自东向西”这个要求。
我们可以将每个城市排序后放到链表里,然后自东向西逐个处理,取前驱,前驱的前驱,后缀,后缀的后缀比较即可。之后删除该点,就可以了。
对答案的处理我写的比较蠢,各种情况分类讨论,应该还有更简洁的方案。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N = 1e5 + 10;
inline int abs(int x) { return x > 0 ? x : -x; }
struct City {
int id, h;
bool operator< (const City &x) const {
return h < x.h;
}
} c[N];
struct node {
int id, h;
bool operator< (const node &x) const {
return abs(h) == abs(x.h) ? h < x.h : abs(h) < abs(x.h);
}
} tmp[5];
int n, h[N], tot, m;
int hd[N], nxt[N], pre[N];
int tar[N][2];
int dis[N][2][20], to[N][20];
int as, bs, ans;
inline void addx(int x, int p) {
tmp[tot].h = c[p].h - h[x];
tmp[tot].id = c[p].id;
tot++;
}
inline void del(int p) {
pre[nxt[p]] = pre[p];
nxt[pre[p]] = nxt[p];
}
void calc_tar(int x, int p) {
tot = 0;
memset(tmp, 0, sizeof tmp);
if (pre[p]) {
addx(x, pre[p]);
if (pre[pre[p]]) {
addx(x, pre[pre[p]]);
}
}
if (nxt[p]) {
addx(x, nxt[p]);
if (nxt[nxt[p]]) {
addx(x, nxt[nxt[p]]);
}
}
std::sort(tmp, tmp+tot);
if (tmp[0].id) tar[x][0] = tmp[0].id;
if (tmp[1].id) tar[x][1] = tmp[1].id;
del(p);
}
void init() {
for (int i = 1; i <= n; ++i) {
to[i][0] = tar[i][1];
dis[i][0][0] = abs(h[i]-h[to[i][0]]);
dis[i][1][0] = 0;
}
for (int i = 1; i <= n; ++i) {
to[i][1] = tar[to[i][0]][0];
dis[i][0][1] = dis[i][0][0];
dis[i][1][1] = abs(h[to[i][0]] - h[to[i][1]]);
}
for (int i = 2; i <= 18; ++i) {
for (int j = 1; j <= n; ++j) {
to[j][i] = to[to[j][i-1]][i-1];
dis[j][0][i] = dis[j][0][i-1] + dis[to[j][i-1]][0][i-1];
dis[j][1][i] = dis[j][1][i-1] + dis[to[j][i-1]][1][i-1];
}
}
}
inline void work(int st, int mxd) {
int sa = 0, sb = 0;
int sst = st;
for (int i = 18; i >= 0; --i) {
if (!to[sst][i] || mxd < dis[sst][0][i] + dis[sst][1][i]) continue;
sa += dis[sst][0][i];
sb += dis[sst][1][i];
mxd -= dis[sst][0][i] + dis[sst][1][i];
sst = to[sst][i];
}
if (bs == 0) {
if (sb == 0) {
if (h[st] > h[ans]) ans = st;
} else ans = st;
as = sa; bs = sb;
return;
}
if (sb == 0) return;
if ((double)as / bs > (double)sa / sb) {
as = sa;
bs = sb;
ans = st;
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &c[i].h);
c[i].id = i;
h[i] = c[i].h;
}
std::sort(c+1, c+n+1);
for (int i = 1; i <= n; ++i) {
hd[c[i].id] = i;
pre[i] = i-1;
nxt[i] = i+1;
}
nxt[n] = 0;
for (int i = 1; i <= n; ++i) {
calc_tar(i, hd[i]);
}
init();
int x, y;
scanf("%d", &x);
for (int i = 1; i <= n; ++i) {
work(i, x);
}
printf("%d\n", ans);
scanf("%d", &m);
for (int i = 1; i <= m; ++i) {
scanf("%d%d", &x, &y);
as = bs = 0;
work(x, y);
printf("%d %d\n", as, bs);
}
return 0;
}