城市网络(树上倍增)

题目:传送门

题意

在一个有 n 个城市的城市网络上,首都城市是 1 号城市,第 i 个城市会售价格为 ai 的珠宝,现在, 有 q 次行程,每次行程从 u 节点到 v 节点,保证 v 在 u 去首都的最短路的路上,你手上有价格为 c 的珠宝,如果你到达某个城市,它出售的珠宝的价格比你手上的所有珠宝都高,那你就会在这个城市买进珠宝,问你从 u 去 v 的最短路上,你会买多少珠宝,在 u, v 也可以买进珠宝。

2 <= n <= 1e5,  1 <= q <= 1e5

思路

邓老师精彩讲解 -> GOGOGO

令 f[i][j] 表示从 i 往上走,能买进珠宝的第 2^j 的城市是哪个。

我们需要先求一个 f[i][0], 其他的  f[i][j] = f[ f[i][j - 1] ][ j - 1 ];

考虑怎么求 f[i][0],我们可以用倍增算,考虑从 i 的父亲节点开始跳,具体看代码实现。

然后,关于 q 次行程,因为一开始手上有价值为 c 的珠宝,所以我们可以在 u 节点多加一个儿子,令它的价值为 c,然后每次就相当于求 u 的这个儿子,到 v 这个城市,有多少个城市需要买进。

这里根据得到的 f[i][j] 一步一步跳,显然不现实,我们可以求出每个城市的深度 du[i],然后仍然是倍增的往上跳,具体实现看代码。

#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define UI unsigned int
#define mem(i, j) memset(i, j, sizeof(i))
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k; i >= j; i--)
#define pb push_back
#define make make_pair
#define INF 0x3f3f3f3f
#define inf LLONG_MAX
#define PI acos(-1)
#define fir first
#define sec second
#define lb(x) ((x) & (-(x)))
#define dbg(x) cout<<#x<<" = "<<x<<endl;
using namespace std;

const int N = 2e5 + 5;

int n, q, a[N], to[N], f[N][21], du[N];

vector < int > G[N];

void dfs(int now, int pre) {

    int x = pre;

    dep(i, 0, 19) {

        if( f[x][i] && a[f[x][i]] <= a[now] ) x = f[x][i]; /// 若 f[x][i] 存在,且这个节点的珠宝价值比 a[now] 小,就跳到这个点,再往上跳

    }

    if(a[x] > a[now]) f[now][0] = x; ///判断哪个城市的珠宝价值大

    else f[now][0] = f[x][0]; 

    rep(i, 1, 19) f[now][i] = f[f[now][i - 1]][i - 1]; ///倍增求 f[i][j]

    du[now] = du[pre] + 1; /// 维护深度

    for(auto v : G[now]) if(v != pre) dfs(v, now); /// 接着往下搜

}

void solve() {

    scanf("%d %d", &n, &q);

    rep(i, 1, n) scanf("%d", &a[i]);

    rep(i, 1, n - 1) {

        int u, v;

        scanf("%d %d", &u, &v);

        G[u].pb(v); G[v].pb(u);

    }

    rep(i, n + 1, n + q) {

        int u, v, w;

        scanf("%d %d %d", &u, &v, &w);

        a[i] = w;

        G[u].pb(i); G[i].pb(u);  ///在节点 u 加一个儿子
 
        to[i] = v; /// 标记终点

    }

    dfs(1, 0);

    rep(i, 1, q) {

        int ans = 0;

        int now = i + n;

        dep(j, 0, 19) {

            if(du[f[now][j]] >= du[to[i + n]]) { /// 若深度大于等于终点的深度,就跳到这个点,算贡献,再接着玩下跳

                ans += (1 << j);

                now = f[now][j];

            }

        }

        printf("%d\n", ans);

    }

}


int main() {

//    int _; scanf("%d", &_);
//    while(_--) solve();

    solve();

    return 0;
}

 

posted on 2020-09-11 14:24  Willems  阅读(159)  评论(0编辑  收藏  举报

导航