城市网络(树上倍增)
题目:传送门
题意
在一个有 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; }
一步一步,永不停息