一个很标准的淀粉质题。
题目所给的式子,我们可以看做一个树的重心,然后周围点与这个重心的贡献和,然后继续找这个重心的子树的重心,重复上述过程,就是淀粉质了。
但是我们需要注意一点,如果我们在计算贡献时,没有对所给式子的第一项进行从小到大的排序的话,时间复杂度是要退化为N^2的。关于贡献的求法,就是一个点所有子树的距离和,加上这些点的个数乘于该点到重心的距离就是贡献。
那么我们很愉快地解决了这道题,下次再见!!!
#include <bits/stdc++.h> const int P = 998244353; int main() { std::cin.tie(0); std::ios::sync_with_stdio(false); int n; std::cin >> n; std::vector <int> val(n); std::vector <int> e[n]; for(int i = 0; i < n; ++i) std::cin >> val[i]; for(int i = 0; i < n - 1; ++i) { int u, v; std::cin >> u >> v; u--, v--; e[u].emplace_back(v); e[v].emplace_back(u); } int root = -1; long long ans = 0; std::vector <int> siz(n); std::vector <int> max_siz(n); std::vector <int> vis(n,0); std::vector <std::pair <int, int>> temp; std::function <void(int, int, int)> get_root = [&] (int u, int fa, int res) { siz[u] = 1; max_siz[u] = 0; for(auto v : e[u]) { if(v == fa || vis[v]) continue; get_root(v, u, res); siz[u] += siz[v]; max_siz[u] = std::max(max_siz[u], siz[v]); } max_siz[u] = std::max(max_siz[u], res - siz[u]); if(root == -1 || max_siz[u] < max_siz[root]) root = u; }; std::function <void(int, int, int)> get_dis = [&] (int u, int fa, int dep) { temp.push_back({val[u],dep}); for(auto v : e[u]) { if(v == fa || vis[v]) continue; get_dis(v, u, dep + 1); } }; auto cal = [&] (int u, int fa, int dep) { temp.clear(); long long res =0 ; get_dis(u, fa, dep); std::sort(temp.begin(),temp.end()); long long dis_sum = 0, dis_pre =0; for(auto v : temp) { dis_sum += v.second; } int len = (int) temp.size(); for(int i = 0; i <len; ++i) { dis_pre += temp[i].second; res = (res + 1ll * temp[i].first * (dis_sum - dis_pre) % P) % P; res = (res + 1ll * temp[i].first * temp[i].second % P * (len - i - 1) % P) % P; } return 2ll * res % P; }; std::function <void(int)> solve = [&] (int u) { ans += cal(u, -1, 0); ans %= P; vis[u] = 1; for(auto v : e[u]) { if(vis[v]) continue; ans -= cal(v, u, 1); ans += P; ans %= P; root = -1; get_root(v, u, siz[v]); solve(root); } }; get_root(0, -1, n); solve(root); std::cout << ans << '\n'; return 0; }