NC13331 城市网络

题目地址:

https://ac.nowcoder.com/acm/problem/13331

基本思路:

  1. 由于数据更新我们的旧解法已经完美的TLE了;
  2. 所以新的思路肯定不能暴力了,我们还是抓住保证 v 在 u 前往首都的最短路径上这句话,使用倍增优化;
  3. 不理解倍增的同学可以上网搜一搜倍增,学习一下,其实就是有点类似于并查集中find操作进化,递推式为 f[s][i] = f[f[s][i - 1]][i - 1], i 代表向上走 2^i 步;
  4. 那么我们的倍增数组记录的是什么呢,我们的倍增数组记录,祖先节点中比第一个当前的节点值大的节点,为倍增数组中该节点的上一个节点;
  5. 那么我们通过倍增的寻找 u -> v 路线在倍增数组里要向上走的步数即是答案;
  6. 为了方便计算,我们将查询也作为一个节点,位置在查询时的u节点下方,大小为给出的初始宝石价格,具体细节可以看代码注释。

参考代码:

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define IO std::ios::sync_with_stdio(false)
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define per(i, l, r) for (int i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
#define INF 0x3f3f3f3f
inline int read() {
  int x = 0, neg = 1; char op = getchar();
  while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
  while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
  return neg * x;
}
inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}
const int maxn = 2e5 + 10;
int n,q,w[maxn],f[maxn][20],dep[maxn],to[maxn];
vector<int> G[maxn];
void dfs(int s,int parent) {
  dep[s] = dep[parent] + 1;//更新深度;
  int x = parent;
  for (int i = 19; i >= 0; i--) {
    if (w[f[x][i]] <= w[s] && f[x][i]) x = f[x][i];
    //往上倍增寻找第一个比w[s]大的节点下面那个节点(他的父亲就是第一个比w[s]大的节点);
  }
  if (w[x] > w[s]) f[s][0] = x;//没找到那这个就是最大的那个;
  else f[s][0] = f[x][0];
  for (int i = 1; i < 20; i++) {
    f[s][i] = f[f[s][i - 1]][i - 1];//更新倍增数组;
  }
  for (auto v : G[s]) {
    if (v == parent) continue;
    dfs(v, s);
  }
}
signed main() {
  n = read(),q = read();
  rep(i, 1, n) w[i] = read();
  rep(i, 1, n - 1) {
    int u, v;
    u = read(),v = read();
    G[u].push_back(v);
    G[v].push_back(u);
  }
  rep(i, n + 1, n + q) {//这里我们将查询作为一个节点,连在u节点下方,方便寻找答案;
    int x, y, z;
    x = read(),y = read(),z = read();
    G[i].push_back(x);
    G[x].push_back(i);
    w[i] = z;
    to[i - n] = y;//这个查询时的u节点对应的v节点;
  }
  dfs(1, 0);
  rep(i, 1, q) {
    int ans = 0;
    int x = i + n;//这次查询时u节点在图中代表的节点;
    for (int j = 19; j >= 0; j--) {
      if (dep[f[x][j]] >= dep[to[i]]) {//从u往上倍增找,位置不能超过v的位置;
        ans += (1 << j);//往上走了几步,即宝石更新了几次;
        x = f[x][j];
      }
    }
    cout << ans << endl;
  }
  return 0;
}