NOIP2012提高组开车旅行 倍增

Posted CzYoL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP2012提高组开车旅行 倍增相关的知识,希望对你有一定的参考价值。

题目分析

朴素的做法就是预处理下一个目的地,然后跑模拟,超时。

本题最重要的考点是倍增优化。设$fa[i][j]$表示a从i出发行驶$2^j$“次”后行驶的路程,$fb[i][j]$表示从i出发行驶$2^j$“次”后行驶的路程,注意这里的"次",a、b交替行驶。$f[i][j]$表示从i出发a、b交替$2^j$“次”后行驶到的城市编号。

显然有$fa[i][j] = fa[i][j - 1] + fa[f[i][j - 1]][j - 1], fb = fb[i][j - 1] + fb[f[i][j - 1]], f[i][j] = ff[i][j - 1]][j - 1]$。只需要用set求出前驱后继和次前驱后继,就能正确预处理出来。最后在路径上跑倍增就行。

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<set>
using namespace std;

const int N = 1e5 + 5;
int n, x0, m, nxta[N], nxtb[N], f[N][30];
typedef long long ll;
ll fa[N][30], fb[N][30];
struct node2{
    int h, pos;
    inline bool operator < (const node2 &b) const{
        return h < b.h;
    }
}data[N];
struct node{
    int pos, dis;
    inline bool operator < (const node &b) const{
        if(dis != b.dis) return dis < b.dis;
        return data[pos].h < data[b.pos].h;
    }
}t[10];
set<node2> S;

inline int read(){
    int i = 0, f = 1; char ch = getchar();
    for(; (ch < 0 || ch > 9) && ch != -; ch = getchar());
    if(ch == -) f = -1, ch = getchar();
    for(; ch >= 0 && ch <= 9; ch = getchar())
        i = (i << 3) + (i << 1) + (ch - 0);
    return i * f;
}

inline void wr(ll x){
    if(x < 0) putchar(-), x = -x;
    if(x > 9) wr(x / 10);
    putchar(x % 10 + 0);
}

inline void Find(int x){
    int    cnt = 0;
    set<node2>::iterator it = S.find(data[x]);
    if(it != S.begin()){
        --it;
        t[++cnt] = (node){it->pos, abs(data[x].h - it->h)};

        if(it != S.begin())        {
            --it;
            t[++cnt] = (node){it->pos, abs(data[x].h - it->h)};
            ++it;
        }
        ++it;
    }
    if((++it) != S.end()){
        t[++cnt] = (node){it->pos, abs(it->h - data[x].h)};
        if((++it) != S.end()){
            t[++cnt] = (node){it->pos, abs(it->h - data[x].h)};
            --it;
        }
        --it;
    }
    sort(t + 1, t + cnt + 1);
    nxtb[x] = t[1].pos;
    if(cnt > 1)
        nxta[x] = t[2].pos;
}

inline void init(){
    for(int i = 1; i <= n; i++){
        int na = nxta[i], nb = nxtb[na];
        fa[i][0] = na ? abs(data[na].h - data[i].h) : 0;
        fb[i][0] = nb ? abs(data[nb].h - data[na].h) : 0;
        f[i][0] = nb;
    }
    for(int j = 1; j <= 20; j++)
        for(int i = 1; i <= n; i++){
            f[i][j] = f[f[i][j - 1]][j - 1];
            fa[i][j] = fa[i][j - 1] + fa[f[i][j - 1]][j - 1];
            fb[i][j] = fb[i][j - 1] + fb[f[i][j - 1]][j - 1];
        }
}

inline void query(int s, ll x, ll &na, ll &nb){
    for(int i = 20; i >= 0; i--)
        if(f[s][i] && fa[s][i] + fb[s][i] <= x){
            na += fa[s][i], nb += fb[s][i];
            x -= fa[s][i] + fb[s][i];
            s = f[s][i];
        }
    int posa = nxta[s], d = abs(data[s].h - data[posa].h); 
    if(posa &&  d <= x) na += d;
}

int main(){
    n = read();
    for(int i = 1; i <= n; i++) data[i] = (node2){read(), i};
    for(int i = n; i >= 1; i--){
        S.insert(data[i]);
        if(i ^ n) Find(i);
    }
    init();
    x0 = read();
    int ans = 0;
    ll ansa = 0, ansb = 0;
    for(int i = 1; i <= n; i++){
        ll na = 0, nb = 0;
        query(i, x0, na, nb);
        if(!ans || ansa * nb > ansb * na) ans = i, ansa = na, ansb = nb;
    }
    wr(ans), putchar(\n);
    m = read();
    for(int i = 1; i <= m; i++){
        ll na = 0, nb = 0;
        int st = read(), x = read();
        query(st, x, na, nb);
        wr(na), putchar( ), wr(nb), putchar(\n);
    }
    return 0;
}

以上是关于NOIP2012提高组开车旅行 倍增的主要内容,如果未能解决你的问题,请参考以下文章

NOIP2012提高组开车旅行

NOIP2012开车旅行 倍增

[NOIP2012] 提高组 洛谷P1081 开车旅行

2012Noip提高组Day1 T3 开车旅行

$Noip2012 Luogu1081$ 开车旅行(倍增优化$ DP$)

Noip2012 开车旅行