【倍增】【set】[NOIP2012] codevs1199 开车旅行

题目点这里


这题的复杂度基本就在预处理找在每个点A、B分别要开去哪上面了 = =倍增其实很水……

用set维护高度。因为只能从前往后,所以从后往前插入,然后取其前两个和后两个用他们之间的距离排个序,距离最小的是B去的地方,第二小是A去的地方。

记A+B各开一次为一轮。然后令f[i][j]为从i出发,开了2^j轮去了哪。fa[i][j]、fb[i][j]分别表示从a、b出发走2^j轮后开的距离。

于是

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

然后询问和倍增LCA差不多 = = 注意考虑最后只有A能走……对于第一个问枚举起点即可。

另外vijos上的数据很厉害啊,估计得手写treap了2333


#include <cstdio>
#include <iostream>
#include <set>
#include <algorithm>

using namespace std;

int N, x0, M, S, x1;
struct city {
    int pos, high;
    bool operator < (const city &b) const {
        return high < b.high;
    }
}c[100005];
set <city> s;
struct temp {
    int pos, dif;
    bool operator < (const temp &b) const {
        if (dif != b.dif) return dif < b.dif;
        return c[pos].high < c[b.pos].high;
    }
}t[5];

int nexta[100005], nextb[100005], f[100005][20];
long long fa[100005][20], fb[100005][20];

inline void Find(int i)
{
    set <city> :: iterator it = s.find(c[i]);
    
    int add = 0;
    if (it != s.begin()) {
        -- it; t[++ add] = (temp) { it -> pos, abs(it -> high - c[i].high) };
        if (it != s.begin()) {
            -- it; t[++ add] = (temp) { it -> pos, abs(it -> high - c[i].high) }; ++ it;
        }
        ++ it;
    }
    if ((++ it) != s.end()) {
        t[++ add] = (temp) { it -> pos, abs(it -> high - c[i].high) };
        if ((++ it) != s.end()) t[++ add] = (temp) { it -> pos, abs(it -> high - c[i].high) };
    }
    
    sort(t + 1, t + add + 1);
    nextb[i] = t[1].pos; if (add == 1) return; nexta[i] = t[2].pos;
}

inline void Query(int St, int X, long long &ta, long long &tb)
{
    for (int i = 19; ~i; -- i) if (f[St][i] && fa[St][i] + fb[St][i] <= X) {
        ta += fa[St][i]; tb += fb[St][i];
        X -= fa[St][i] + fb[St][i]; St = f[St][i];
    }
    int posa = nexta[St]; if (!posa) return;
    int dis = abs(c[posa].high - c[St].high); if (dis <= X) ta += dis;
}

int main()
{
    ios :: sync_with_stdio(false);
    cin >> N;
    for (int i = 1; i <= N; ++ i) {
        cin >> c[i].high; c[i].pos = i;
    }
    
    for (int i = N; i; -- i) {
        s.insert(c[i]); if (i ^ N) Find(i);
    }
    for (int i = 1; i <= N; ++ i) {
        int pos1 = nexta[i], pos2 = nextb[nexta[i]];
        fa[i][0] = pos1 ? abs(c[pos1].high - c[i].high) : 0;
        fb[i][0] = pos2 ? abs(c[pos2].high - c[pos1].high) : 0;
        f[i][0] = pos2;
    }
    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];
        }
    }
    
    cin >> x0; int ans = 0;
    long long ansa = 1e15, ansb = 0ll;
    for (int i = 1; i <= N; ++ i) {
        long long ta = 0ll, tb = 0ll; Query(i, x0, ta, tb);
        if (tb && (!ans || ansa * tb > ansb * ta)) {
            ansa = ta; ansb = tb; ans = i;
        }
    }
    cout << ans << endl;
    
    for (cin >> M; M --; ) {
        cin >> S >> x1;
        long long ta = 0ll, tb = 0ll; Query(S, x1, ta, tb);
        cout << ta << " " << tb << endl;
    }
        
    return 0;
}




  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值