CodeChef Children and trips (分块)

题意:
             一棵 n n 个节点没条边权值是1或者 2 2 的树,有m个查询,每次给出 (x,y,z) ( x , y , z ) ,问每次跳跃步数不超过 z z 的情况下,最少跳几次能从x跳到 y y

思路:
     z>n z > n 的时候暴力跳,知道这里不会超过 n n 步,否则预处理出树上的关键点,关键点是指:该点的深度 mod n=0 m o d   n = 0 的点,那么可以知道关键点之间的跳跃不会超过 n n 步,这样预处理每两个关键点在步数小于 n n 的时候从一个关键点到上一个关键点需要跳多少次,最终剩余多少步没跳完,这样查询 x x lca(x,y)得出最少跳的次数和最后剩余的步数,对 y y <script type="math/tex" id="MathJax-Element-2142">y</script>也是如此,处理两条链的情况就行了。细节很多。。。

#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 1e5 + 10;
using namespace std;

typedef pair<int, int> pa;
vector<pa> vec[maxn];
int anc[maxn][18], deep[maxn];
int n, m, dist[maxn], block_size = 350;
int sz[maxn], key[maxn], bel[maxn];
int can_arrive[maxn][352]; ///当前点步长为z的时候,下一步的位置
pa jump[maxn][352];  ///当前点跳到下一个关键点,步长为z的时候最少需要first步,到达关键点还剩余second单位长

void dfs(int x, int fa, int d, int ds, int tot_dis) {
    anc[x][0] = fa; deep[x] = d;
    dist[x] = tot_dis; sz[x] = 1;
    for(int i = 1; i < 18; i++) {
        int t = anc[x][i - 1];
        if(~t) anc[x][i] = anc[t][i - 1];
        else break;
    }
    for(int i = 0; i <= block_size; i++) {
        if(i >= ds) can_arrive[x][i] = can_arrive[fa][i - ds];
        else can_arrive[x][i] = x;
        if(!can_arrive[x][i]) can_arrive[x][i] = 1;
    }
    for(int i = 0; i < vec[x].size(); i++) {
        pa e = vec[x][i];
        if(e.first != fa) dfs(e.first, x, d + 1, e.second, tot_dis + e.second);
        if(e.first != fa) sz[x] += sz[e.first];
    }
    if(deep[x] % 175 == 0 && sz[x] >= 175) key[x] = 1;
}

int query_anc(int x, int y) {
    if(deep[x] < deep[y]) swap(x, y);
    for(int i = 17; i >= 0; i--) {
        if(deep[x] - (1 << i) < deep[y]) continue;
        x = anc[x][i];
    }
    if(x == y) return x;
    for(int i = 17; i >= 0; i--) {
        if(anc[x][i] == anc[y][i]) continue;
        x = anc[x][i]; y = anc[y][i];
    }
    return anc[x][0];
}

void init_jump(int x, int fa, int im) {
    bel[x] = im;
    for(int i = 0; i <= block_size; i++) {
        int tot_dis = dist[x] - dist[bel[x]]; ///跳下一个关键点
        if(i >= tot_dis) {
            jump[x][i].first = 1;
            jump[x][i].second = i - tot_dis;
        } else {
            int nxt = can_arrive[x][i];
            if(nxt == x) continue;
            jump[x][i].first = jump[nxt][i].first + 1;
            jump[x][i].second = jump[nxt][i].second;
        }
    }
    for(int i = 0; i < vec[x].size(); i++) {
        pa e = vec[x][i];
        if(e.first == fa) continue;
        if(key[x]) init_jump(e.first, x, x);
        else init_jump(e.first, x, im);
    }
}

void solve(int &x, int to, pa &now, int len) {
    while(x != to) {
        if(dist[x] - dist[anc[x][0]] > now.second) { now.second = len; now.first++; }
        now.second -= dist[x] - dist[anc[x][0]];
        x = anc[x][0];
    }
}

pa jump_to_lca(int x, int lca, int len) {
    pa now = pa(0, 0);
    while(bel[x] != bel[lca]) {
        int b = bel[x];
        int tot_dis = dist[x] - dist[b]; ///当前点到关键点的距离
        if(len >= block_size) { ///暴力跳
            if(dist[x] - dist[b] > block_size) {
                solve(x, b, now, len);
            } else {
                while(now.second < dist[x] - dist[b]) {
                    x = can_arrive[x][now.second];
                    now.second = len; now.first++;
                }
                now.second -= dist[x] - dist[b];
                x = b;
            }

        } else {
            if(deep[can_arrive[x][now.second]] < deep[lca]) break;
            int tx = can_arrive[x][now.second];
            now.second -= dist[x] - dist[tx];
            x = tx;
            if(bel[x] == b) {
                now.first += jump[x][len].first;
                now.second = jump[x][len].second;
                x = b;
            }
        }
    }
    if(deep[x] >= deep[lca]) solve(x, lca, now, len);
    else now.second = dist[lca] - dist[x];
    return now;
}

int query(int x, int y, int z) {
    int lca = query_anc(x, y);
    pa info_x = jump_to_lca(x, lca, z);
    pa info_y = jump_to_lca(y, lca, z);
    if(info_x.second == z) { info_x.first--; info_x.second = 0; }
    if(info_y.second == z) { info_y.first--; info_y.second = 0; }
    if(!info_x.second || !info_y.second) return info_x.first + info_y.first;
    int rx = info_x.second, ry = z - info_y.second;
    if(rx >= ry) return info_x.first + info_y.first - 1;
    else return info_x.first + info_y.first;
}

const int MAX = 10000;
char buf[MAX], *ps = buf, *pe = buf + 1;
inline void rnext() {
    if(++ps == pe) pe = (ps = buf) + fread(buf, sizeof(char), sizeof(buf) / sizeof(char), stdin);
}
template <class T>
inline bool in(T &ans) {
    ans = 0;
    T f = 1;
    if(ps == pe) return false;//EOF
    do{
        rnext();
        if('-' == *ps) f = -1;
    } while(!isdigit(*ps) && ps != pe);
    if(ps == pe) return false;//EOF
    do {
        ans = (ans<<1)+(ans<<3)+*ps-48;
        rnext();
    } while(isdigit(*ps) && ps != pe);
    ans *= f;
    return true;
}

int main() {
    in(n);
    for(int i = 0; i < maxn; i++) vec[i].clear();
    memset(anc, -1, sizeof anc);
    for(int i = 0; i < n - 1; i++) {
        int u, v, c;
        in(u); in(v); in(c);
        vec[u].push_back(pa(v, c));
        vec[v].push_back(pa(u, c));
    }
    dfs(1, 0, 1, 0, 0);
    key[1] = 1; bel[1] = 1;
    init_jump(1, 0, 0);
    in(m);
    while(m--) {
        int x, y, z;
        in(x); in(y); in(z);
        int ans = query(x, y, z);
        printf("%d\n", ans);
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值