ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LOJ #6733. 人造情感

2022-01-30 19:04:15  阅读:173  来源: 互联网

标签:dep fa LOJ 人造 int 6733 Maxn operatorname mod


LOJ #6733. 人造情感

​ 先考虑如何求解 \(W(S)\)。设 \(f_u\) 为考虑子树 \(u\) 内的路径集合的 \(W\) 值,则有转移

\[f_u=\max\left\{\sum_{v\in \operatorname{ch}_u}f_v\right\}\cup\left\{w+\sum_{v\notin \operatorname{path}(x,y)\and\operatorname{fa}_v\in \operatorname{path}(x,y)}f_v\right\} \]

其中 \((x,y,w)\) 在给定的路经集合 \(U\) 中,且满足条件 \(\operatorname{lca}(x,y)=u\)。直接做显然就暴毙了,可以思考转移的性质。

​ 观察发现这个转移涉及到“路径下方所挂的点”的求和,于是我们可以想到树上差分。令 \(f'_u=f_u-\sum\limits_{v\in\operatorname{ch}_u}f_v\),在上式中用 \(f'\) 替代 \(f\),于是转移就变成了:

\[f'_u=\max\{0\}\cup\left\{w-\sum_{v\in \operatorname{path}(x,y)\and v\neq u}f'_v\right\} \]

其中 \((x,y,w)\) 在给定的路经集合 \(U\) 中,且满足条件 \(\operatorname{lca}(x,y)=u\)。

​ 于是现在只需实现单点加链求和就行了,可以用树状数组维护 \(\operatorname{dfn}\) 序,时间复杂度为 \(\mathcal O(n\log n)\)。

​ 接下来考虑如何计算 \(f(x,y)\)。注意到 \(f(x,y)=f_\mathrm{root}-h_{\operatorname{lca}(x,y)}-\sum\limits_{u\notin\operatorname{path}(x,y)\and \operatorname{fa}_u\in\operatorname{path}(x,y)}f_{u}\),其中 \(h_u\) 为考虑子树 \(u\) 外的路径集合的 \(W\) 值。后面涉及到“路径下方所挂的点”的求和可以直接用树上差分消掉,有 \(f(x,y)=f_{\mathrm{root}}+\sum\limits_{u\in\operatorname{path}(x,y)}f'_u-f_{\operatorname{lca}(u,v)}-h_{\operatorname{lca}(u,v)}\)。前面有关 \(f,f'\) 的值是很容易就能求出来的,关键就是如何计算 \(h_{u}\)。

​ 类似于处理 \(f\) 的方法,我们设 \(h'_u=h_u-h_{\operatorname{fa}_u}-\sum\limits_{v\in\operatorname{ch}_{\operatorname{fa}_u}\and v\neq u}f_{v}\),那么 \(h'\) 有和 \(f'\) 类似的转移式。我们考虑枚举所有经过 \(\operatorname{fa}_u\) 但不经过 \(u\) 的路径 \((x,y,w)\),设 \(z=\operatorname{lca}(x,y)\)。对于当前枚举的 \(u\),设 \(g'_v=\begin{cases}f'_v,\qquad v不是u的祖先\\h'_v,\qquad v是u的祖先\end{cases}\),容易发现路径 \((x,y,w)\) 对 \(h'_u\) 的贡献为 \(w-\sum\limits_{v\in \operatorname{path}(x,y)\and v\neq z}g'_v\)。于是我们就得到了一个朴素的做法:先枚举结点 \(u\),然后枚举路径集合 \(U\) 中经过 \(u\) 的路径 \((x,y,w)\),在更新 \(g'\) 的同时计算贡献。该做法的时间复杂度为 \(\mathcal O(n^2)\)。

​ 考虑优化。注意到 \(U\) 中的路径 \((x,y,w)\) 只会对其下方挂着的所有点造成贡献;更进一步,其对同一个结点下挂着的所有儿子的贡献是一样的。设结点 \(u\) 在路径 \((x,y)\) 上,结点 \(v\) 为结点 \(u\) 的一个儿子,且满足 \(v\) 不在路径 \((x,y)\) 上。设 \(z=\operatorname{lca}(x,y)\),那么该路径对 \(v\) 的贡献为

\[\begin{cases}w-(F_x-F_z)-(F_y-F_z),\qquad\qquad\qquad\qquad\! &(u=z)\\w-(F_x-F_u)-(F_y-F_z)-(H_u-H_z),&(u\in\operatorname{path}(x,z)\and u\neq z)\\w-(F_x-F_z)-(F_y-F_u)-(H_u-H_z),&(u\in\operatorname{path}(y,z)\and u\neq z)\end{cases} \]

其中 \(F,H\) 分别为 \(f',h'\) 在树上的祖先和,即有 \(F_x=\sum\limits_{y\in\operatorname{path}(\mathrm{root},x)}f'_y\)。上式中的第二类和第三类可以合在一起讨论,因此只需要讨论 \(u=z\) 和 \(u\neq z\) 的情况。

  • \(u=z\) 此时路径 \((x,y,w)\) 对 \(z\) 的子结点 \(v\) 有贡献当且仅当 \(v\) 不在路径 \((x,y)\) 上。将 \(z\) 的所有子结点看成一个序列,那么该路径在序列上的影响可分为 \(1/2/3\) 个区间。子结点序列上需要实现的操作是区间取 \(\max\) 单点查询。因此只需要使用线段树维护 \(z\) 的子结点序列即可。
  • \(u\neq z\) 不妨设 \(u\in\operatorname{path}(x,z)\)。此时路径 \((x,y,w)\) 对 \(u\) 的子结点 \(v\) 有贡献当且仅当 \(v\) 不在路径 \((x,y)\) 上。考虑枚举结点 \(u\) 时计算 \(h'_v\),那么对 \(h'_v\) 有贡献的路径 \((x,y,w)\) 一定存在端点 \(x\) 在 \(u\) 子树中而不在 \(v\) 子树中。我们可以在结点 \(x,y\) 上存下路径 \((x,y,w)\) 的贡献,查询时转到 \(\operatorname{dfn}\) 序上就是一个区间查询。因此我们可以用线段树维护 \(\operatorname{dfn}\) 序。

时间复杂度为 \(\mathcal O((n+m)\log n)\)。

参考代码

#include <bits/stdc++.h>
using namespace std;
template<typename _Tp> _Tp &min_eq(_Tp &x, const _Tp &y) { return x = min(x, y); }
template<typename _Tp> _Tp &max_eq(_Tp &x, const _Tp &y) { return x = max(x, y); }
static constexpr int mod = 998244353;
static constexpr int Maxn = 3e5 + 5;
static constexpr int64_t inf = 0x3f3f3f3f3f3f3f3f;
int n, m;
int64_t ans;
vector<int> g[Maxn];
namespace hld {
  int par[Maxn], sz[Maxn], son[Maxn], dep[Maxn];
  int top[Maxn], dfn[Maxn], idfn[Maxn], ed[Maxn], dn;
  void predfs1(int u, int fa, int depth) {
    par[u] = fa, dep[u] = depth; sz[u] = 1, son[u] = 0;
    for (const int &v: g[u]) if (v != par[u]) {
      predfs1(v, u, depth + 1), sz[u] += sz[v];
      if (son[u] == 0 || sz[v] > sz[son[u]]) son[u] = v;
    }
  } // hld::predfs1
  void predfs2(int u, int topv) {
    top[u] = topv, idfn[dfn[u] = ++dn] = u;
    if (son[u] != 0) predfs2(son[u], topv);
    for (const int &v: g[u]) if (v != par[u])
      if (v != son[u]) predfs2(v, v);
  } // hld::predfs2
  inline int get_lca(int u, int v) {
    for (; top[u] != top[v]; v = par[top[v]])
      if (dep[top[u]] > dep[top[v]]) swap(u, v);
    return dep[u] < dep[v] ? u : v;
  } // hld::get_lca
  inline int get_anc(int u, int k) {
    if (k < 0 || k >= dep[u]) return 0;
    for (; dep[u] - dep[par[top[u]]] <= k; u = par[top[u]])
      k -= (dep[u] - dep[par[top[u]]]);
    return idfn[dfn[u] - k];
  } // hld::get_anc
  inline void initialize(int root) {
    dn = 0, predfs1(root, 0, 1), predfs2(root, root);
    for (int i = 1; i <= n; ++i) ed[i] = dfn[i] + sz[i] - 1;
  } // hld::initialize
} // namespace hld
using namespace hld;
namespace fen {
  int64_t b[Maxn];
  inline void clr(void) { memset(b, 0, sizeof(b)); }
  inline void upd(int x, int64_t v) { for (; x <= n; x += x & -x) b[x] += v; }
  inline int64_t ask(int x) { int64_t r = 0; for (; x; x -= x & -x) r += b[x]; return r; }
} // namespace fen
struct path { int x, y; int64_t w; } pa[Maxn];
vector<path> plca[Maxn];
int64_t f[Maxn], F[Maxn], f1[Maxn];
void dfs1(int u, int fa) {
  for (const int &v: g[u]) if (v != fa) dfs1(v, u);
  for (const auto &[x, y, w]: plca[u])
    max_eq(f[u], w - fen::ask(dfn[x]) - fen::ask(dfn[y]));
  fen::upd(dfn[u], f[u]), fen::upd(ed[u] + 1, -f[u]);
} // dfs1
void dfs11(int u, int fa) {
  F[u] = f[u] + F[fa], f1[u] = f[u];
  for (const int &v: g[u]) if (v != fa)
    dfs11(v, u), f1[u] += f1[v];
} // dfs11
int64_t h[Maxn], H[Maxn], h1[Maxn];
namespace sgt1 {
  int64_t tr[Maxn * 4];
  void update(int p, int l, int r, int x, int64_t v) {
    max_eq(tr[p], v);
    if (l == r) return ; int mid = (l + r) / 2;
    if (x <= mid) update(p * 2 + 0, l, mid, x, v);
    else update(p * 2 + 1, mid + 1, r, x, v);
  } // sgt1::update
  int64_t query(int p, int l, int r, int L, int R) {
    if (L > r || l > R) return -inf;
    if (L <= l && r <= R) return tr[p];
    int mid = (l + r) / 2;
    return max(query(p * 2 + 0, l, mid, L, R), query(p * 2 + 1, mid + 1, r, L, R));
  } // sgt1::query
  inline void upd(int x, int64_t v) { return update(1, 1, n, x, v); }
  inline int64_t ask(int l, int r) { return query(1, 1, n, l, r); }
} // namespace sgt1
namespace sgt2 {
  int N; int64_t tr[Maxn * 4];
  void build(int n) { N = n, memset(tr, -63, (n + 1) * 4 * sizeof(*tr)); }
  void update(int p, int l, int r, int L, int R, int64_t v) {
    if (L > r || l > R) return ;
    if (L <= l && r <= R) return max_eq(tr[p], v), void();
    int mid = (l + r) / 2;
    update(p * 2 + 0, l, mid, L, R, v);
    update(p * 2 + 1, mid + 1, r, L, R, v);
  } // sgt2::update
  int64_t query(int p, int l, int r, int x) {
    if (l == r) return tr[p];
    int mid = (l + r) / 2; int64_t t = tr[p];
    if (x <= mid) max_eq(t, query(p * 2 + 0, l, mid, x));
    else max_eq(t, query(p * 2 + 1, mid + 1, r, x));
    return t;
  } // sgt2::query
  inline void upd(int l, int r, int64_t v) { return update(1, 1, N, l, r, v); }
  inline int64_t ask(int x) { return query(1, 1, N, x); }
} // namespace sgt2
void dfs2(int u, int fa) {
  H[u] = H[fa] + h[u];
  static int label[Maxn]; int N = 0;
  for (const int &v: g[u]) if (v != fa) label[v] = ++N;
  if (N != 0) {
    for (const int &v: g[u]) if (v != fa)
      max_eq(h[v], max(sgt1::ask(dfn[u], dfn[v] - 1), sgt1::ask(ed[v] + 1, ed[u])) + F[u] - H[u]);
    sgt2::build(N);
    for (auto [x, y, w]: plca[u]) {
      if (dep[x] > dep[y]) swap(x, y);
      int xk = get_anc(x, dep[x] - dep[u] - 1);
      int yk = get_anc(y, dep[y] - dep[u] - 1);
      if (xk == 0 && yk == 0) {
        sgt2::upd(1, N, w);
      } else if (xk == 0) {
        int64_t v = w - F[y] + F[u];
        if (1 < label[yk]) sgt2::upd(1, label[yk] - 1, v);
        if (label[yk] < N) sgt2::upd(label[yk] + 1, N, v);
      } else {
        int64_t v = w - F[x] + F[u] - F[y] + F[u];
        if (label[xk] > label[yk]) swap(x, y), swap(xk, yk);
        if (1 < label[xk]) sgt2::upd(1, label[xk] - 1, v);
        if (label[yk] < N) sgt2::upd(label[yk] + 1, N, v);
        if (label[xk] + 1 <= label[yk] - 1) sgt2::upd(label[xk] + 1, label[yk] - 1, v);
      }
    }
    for (const int &v: g[u]) if (v != fa)
      max_eq(h[v], sgt2::ask(label[v]));
  }
  for (auto [x, y, w]: plca[u]) {
    if (dep[x] > dep[y]) swap(x, y);
    int xk = get_anc(x, dep[x] - dep[u] - 1);
    int yk = get_anc(y, dep[y] - dep[u] - 1);
    if (xk == 0 && yk == 0) {
    } else if (xk == 0) {
      int64_t v = w - F[y] + H[u];
      sgt1::upd(dfn[y], v);
    } else {
      int64_t v = w - F[x] - F[y] + H[u] + F[u];
      sgt1::upd(dfn[x], v);
      sgt1::upd(dfn[y], v);
    }
  }
  for (const int &v: g[u]) if (v != fa) dfs2(v, u);
} // dfs2
void dfs3(int u, int fa) {
  for (const int &v: g[u]) if (v != fa)
    h1[v] = h1[u] + f1[u] - f1[v] - f[u] + h[v], dfs3(v, u);
} // dfs3
int64_t sf[Maxn];
void dfs4(int u, int fa) {
  for (const int &v: g[u]) if (v != fa) dfs4(v, u), (sf[u] += sf[v]) %= mod;
  int64_t z = (int64_t)sz[u] * sz[u];
  for (const int &v: g[u]) if (v != fa) z -= (int64_t)sz[v] * sz[v];
  ((ans -= (z % mod) * ((f1[u] + h1[u]) % mod) % mod) += mod) %= mod;
  for (const int &v: g[u]) if (v != fa) (ans += 2 * sf[v] * (sz[u] - sz[v]) % mod) %= mod;
  (ans += (f[u] % mod) * (z % mod) % mod) %= mod;
  (sf[u] += sz[u] * f[u] % mod) %= mod;
} // dfs4
int main(void) {
  scanf("%d%d", &n, &m);
  for (int i = 2; i <= n; ++i) {
    int u, v;
    scanf("%d%d", &u, &v);
    g[u].push_back(v);
    g[v].push_back(u);
  }
  hld::initialize(1);
  for (int i = 1; i <= m; ++i) {
    scanf("%d%d%lld", &pa[i].x, &pa[i].y, &pa[i].w);
    int z = get_lca(pa[i].x, pa[i].y);
    plca[z].push_back(pa[i]);
  }
  fen::clr(), dfs1(1, 0), dfs11(1, 0);
  memset(sgt1::tr, -63, sizeof(sgt1::tr));
  dfs2(1, 0), dfs3(1, 0);
  ans = 0, dfs4(1, 0);
  (ans += (int64_t)n * n % mod * (f1[1] % mod) % mod) %= mod;
  printf("%lld\n", ans);
  exit(EXIT_SUCCESS);
} // main

标签:dep,fa,LOJ,人造,int,6733,Maxn,operatorname,mod
来源: https://www.cnblogs.com/cutx64/p/15857368.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有