题解 | #小 Q 与树#

小 Q 与树

https://ac.nowcoder.com/acm/contest/11171/D

给出一颗树,结点权值为 求:

思路

本题为点分治模板题

以重心为根,用 solve(x) 解决 子树内贡献

每次 solve(x) 时,首先得到 经过该点不经过该点 的贡献总和 calc(x, fa, 0)

这个过程首先利用 dfs_dis(x, fa, 0) 得到以 为根的链信息再将链两两合并,得到 的路径贡献

排除 不经过该点, 即排除 的情况,只需要 先向下走一步,然后统计答案,及 calc(x, fa, 1)

注意到以 为根后会将 删去,则每条路径有且仅会被统计一次,故答案正确。

对于本题,由于求 时若暴力枚举,calc 复杂度会为 总复杂度为

需要先排序处理,calc 复杂度为 总复杂度为

注意: 求重心时 S = sz[x] 每次都要更新,否则复杂度不对

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 + 10;

#define rep(i, s, t) for (int i = (int)(s); i <= (int)(t); ++i)

const int mod = 998244353;

int root, mx[N], sz[N];
int n, m, S, tot;
bool vis[N];
vector<int> G[N];

bool chkmax(int &x, int y) {
  if (x < y) return x = y, 1; return false;
}

int read() {
  int x = 0, ch = getchar();
  while (ch < '0' || ch > '9') ch = getchar();
  while (ch >='0' && ch <='9')
    x = x * 10 + (ch ^ 48), ch = getchar();
  return x;
}

#define int long long
int a[N], ans;
pair<int, int> q[(int)(1e6) + 10];

void dfs_root(int x, int fa) {
  mx[x] = 0, sz[x] = 1;
  for (int y : G[x]) {
    if (y == fa || vis[y]) continue;
    dfs_root(y, x);
    sz[x] += sz[y];
    chkmax(mx[x], sz[y]);
  }
  chkmax(mx[x], S - sz[x]);
  if (mx[x] < mx[root]) root = x;
}

void dfs_dis(int x, int fa, int dis) {
  q[++ tot] = make_pair(a[x], dis);
  for (int y : G[x]) {
    if (y == fa || vis[y]) continue;
    dfs_dis(y, x, dis + 1);
  }
}

int Mod(int x) {
  return (x % mod + mod) % mod;
}

int calc(int x, int fa, int org) {
  int res = 0;
  tot = 0;
  dfs_dis(x, fa, org);
  sort(q + 1, q + tot + 1);
  int dis_sum = 0, dis_prefix = 0;
  rep(i, 1, tot) {
    dis_sum += q[i].second;
  }
  rep(i, 1, tot) {
    dis_prefix += q[i].second;
    res = Mod(res + q[i].first * (dis_sum - dis_prefix));
    res = Mod(res + (tot - i) * q[i].second * q[i].first);
  }
  return Mod(res + res);
}

void solve(int x) {
  tot = 0;
  vis[x] = 1;
  ans += calc(x, 0, 0);
  for (int y : G[x]) {
    if (vis[y]) continue;
    ans = Mod(ans - calc(y, x, 1));
    mx[root = 0] = S = n;
    S = sz[x];
    dfs_root(y, x);
    solve(root);
  }
}

signed main() {
  n = read();
  rep(i, 1, n) a[i] = read();
  rep(i, 1, n - 1) {
    int x = read(), y = read();
    G[x].push_back(y);
    G[y].push_back(x);
  }
  mx[root = 0] = S = n;
  dfs_root(1, 0);
  solve(root);
  printf("%lld", ans);
}
全部评论

相关推荐

不愿透露姓名的神秘牛友
11-26 15:46
已编辑
字节国际 电商后端 24k-35k
点赞 评论 收藏
分享
点赞 评论 收藏
分享
沉淀一会:1.同学你面试评价不错,概率很大,请耐心等待; 2.你的排名比较靠前,不要担心,耐心等待; 3.问题不大,正在审批,不要着急签其他公司,等等我们! 4.预计9月中下旬,安心过节; 5.下周会有结果,请耐心等待下; 6.可能国庆节前后,一有结果我马上通知你; 7.预计10月中旬,再坚持一下; 8.正在走流程,就这两天了; 9.同学,结果我也不知道,你如果查到了也告诉我一声; 10.同学你出线不明朗,建议签其他公司保底! 11.同学你找了哪些公司,我也在找工作。
点赞 评论 收藏
分享
评论
2
收藏
分享
牛客网
牛客企业服务