[NOIp2012]开车旅行

Description

Luogu1081

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;
}

猜你喜欢

转载自www.cnblogs.com/wyxwyx/p/noip201213.html
今日推荐