[Codeforces915F] Imbalance Value of a Tree

给定一棵 (n) 个点的无根树,每个点有点权 (a_i),求路径极差和。

(n leq 10^6)

每个点作为最大值和最小值分别算贡献。

以最大值为例,按点权从小到大排序,每次加点把与这个点和已有点之间的边加上,用并查集维护子树大小,加边时候计数即可。

这样每条边最多被访问两次。

不过还可以优化,因为一条边一定在大的那一端的点加入时被加入,所以把边定一个边权等于 (max(a_x,a_y)),然后从小到大加边即可。

我写的是加点的。

#include <bits/stdc++.h>
#define dbg(...) std::cerr << "33[32;1m", fprintf(stderr, __VA_ARGS__), std::cerr << "33[0m";
template <class T, class U>
inline bool smin(T &x, const U &y) { return y < x ? x = y, 1 : 0; }
template <class T, class U>
inline bool smax(T &x, const U &y) { return x < y ? x = y, 1 : 0; }

using LL = long long;
using PII = std::pair<int, int>;

constexpr int N(1e6 + 5);
int n, a[N], p[N], id[N];
std::vector<int> g[N];

int fa[N], siz[N];
inline int find(int x) {
  while (fa[x] != x) x = fa[x] = fa[fa[x]];
  return x;
}
LL ans;
void solve() {
  std::iota(p + 1, p + 1 + n, 1);
  std::iota(fa + 1, fa + 1 + n, 1);
  std::fill(siz + 1, siz + 1 + n, 1);
  std::sort(p + 1, p + 1 + n, [](int i, int j) { return a[i] < a[j]; });
  for (int i = 1; i <= n; i++) id[p[i]] = i;
  for (int i = 1; i <= n; i++) {
    int x = p[i], v = a[x];
    for (int y : g[x]) {
      if (id[y] > i) continue;
      x = find(x), y = find(y);
      ans += 1LL * siz[x] * siz[y] * v;
      if (siz[x] > siz[y]) std::swap(x, y);
      siz[y] += siz[x], fa[x] = y;
    }
  }
}
int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  std::cin >> n;
  for (int i = 1; i <= n; i++) std::cin >> a[i];
  for (int i = 1, x, y; i < n; i++) {
    std::cin >> x >> y;
    g[x].push_back(y), g[y].push_back(x);
  }
  solve();
  for (int i = 1; i <= n; i++) a[i] = -a[i];
  solve();
  std::cout << ans;
  return 0;
}
原文地址:https://www.cnblogs.com/HolyK/p/14237955.html