题解 | #小 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);
}
全部评论

相关推荐

评论
2
收藏
分享

创作者周榜

更多
正在热议
更多
# 大厂实习和小厂实习最大的区别是什么? #
2378次浏览 20人参与
# 参加完秋招的机械人,还参加春招吗? #
119942次浏览 760人参与
# 米连集团26产品管培生项目 #
14486次浏览 291人参与
# 牛友の3月总结 #
1839次浏览 26人参与
# 这些公司卡简历很严格 #
95209次浏览 417人参与
# 面试被问到不会的问题,你怎么应对? #
688次浏览 8人参与
# 开放七大实习专项,百度暑期实习值得冲吗 #
18791次浏览 302人参与
# 拼多多工作体验 #
52676次浏览 342人参与
# 研究所VS国企,该如何选 #
259065次浏览 2013人参与
# 通信硬件知识分享 #
48135次浏览 538人参与
# 找AI工作可以去哪些公司? #
17071次浏览 750人参与
# 从事AI岗需要掌握哪些技术栈? #
14917次浏览 845人参与
# 你做过最难的笔试是哪家公司 #
47440次浏览 757人参与
# 实习最想跑路的瞬间 #
130955次浏览 740人参与
# 金三银四,你的春招进行到哪个阶段了? #
24576次浏览 297人参与
# 说说你知道的学历厂 #
391006次浏览 1379人参与
# AI面会问哪些问题? #
36173次浏览 1075人参与
# 想给25届机械人的秋招建议 #
47739次浏览 251人参与
# 机械人避雷的岗位/公司 #
62887次浏览 395人参与
# 大厂无回复,继续等待还是奔赴小厂 #
343361次浏览 1988人参与
# 滴!实习打卡 #
814707次浏览 6858人参与
# 我心目中的理想工作是这样的 #
100873次浏览 907人参与
牛客网
牛客网在线编程
牛客网题解
牛客企业服务