1. 程式人生 > 實用技巧 >城市網路(樹上倍增)

城市網路(樹上倍增)

題目:傳送門

題意

在一個有 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; }