Codeforces 1578J. Just Kingdom

Codeforces 1578J. Just Kingdom

不难发现, 子树 \(u\) 的需求量为 \(s_u=\sum\limits_{v\in\text{subt}(u)}w_v\,\), 而题目中所描述的发钱过程则类似于灌水的过程, 其中子节点 \(v\) 对应的水管容量为 \(s_v\,\). 现在考虑若要让子节点 \(v\) 得到 \(s_v\) , 节点 \(u\) 至少需要得到多少水. 将节点 \(u\) 的所有子节点 ( 除了 \(v\) ) 的 \(s\) 值按从低到高排序后得到序列 \(t\,\), 则节点 \(u\) 至少要得到的量为 \(\sum\limits_{t_i\le s_v}t_i+\left(1+\sum\limits_{t_i>s_v}1\right)s_v\,\). 于是得到了一个暴力做法: 对于每一个节点往上跳父节点, 直到根节点. 这样做的时间复杂度是 \(\mathcal O(n^2)\) 的. 更具体一点, 是 \(\mathcal O\left(\sum\limits_{i=1}^n\text{dep}_i\right)\)

注意到 \(\mathcal O\left(\sum\limits_{i=1}^n\text{dep}_i\right)=\mathcal O\left(\sum\limits_{i=1}^n\text{siz}_{i}\right)\), 其中 \(\text{siz}_i\) 表示子树 \(i\) 的大小. 于是想到可以使用树上启发式合并优化. 但是上述的暴力是无法直接用启发式合并优化的. 于是我们考虑一个更贴切的方法.

在每一个节点处存储子树中所有节点目前的答案 ( 即尚未完全加工的答案 ) , 这个节点内要做的事儿就是将所有节点目前的答案加工并上传到其父亲处. 从子节点转移到父节点的过程就是合并多个子树.

考虑树上启发式合并优化上述过程. 使用平衡树来存储当前节点内的所有答案, 那么轻儿子就可以暴力插入一个个值. 最难的问题就是如何将重儿子的信息直接继承到父节点上, 且复杂度必须为 \(\mathcal O\left(\operatorname{polylog}(\text{deg}_u)\right)\). 我们设子节点的 \(s_u\) 序列排序后得到 \(t_u\,\), 那么对于同一个 \(u\,\), 值域区间 \(t_i\le x\le t_{i+1}\) 内所有 \(x\) 的加工方式都是一样的, 都有 \(ax+b\) 的形式. 也就是说要对若干个区间 \([l,r]\), 将这些区间中的值都作用上一次函数. 又观察到在操作完后序列的单调性不变, 于是可以用平衡树区间加乘懒标记维护. 总时间复杂度 \(\mathcal O(n\log^2n)\).

一个坑点: 在继承重儿子时操作顺序应该是值域从大往小进行操作 ( 否则若从小往大进行操作, 则一个数可能被多次操作 ) .

参考代码
#include <bits/stdc++.h>
using namespace std;
static constexpr int Maxn = 3e5 + 5;
static constexpr int64_t inf = 0x3f3f3f3f3f3f3f3f;
__attribute__((__always_inline__)) inline std::uint64_t randu64(void) {
  static std::uint64_t seed = std::chrono::steady_clock::now().time_since_epoch().count();
  seed += 0xa0761d6478bd642full; __uint128_t t = (__uint128_t)(seed ^ 0xe7037ed1a0b428dbull) * seed;
  return (t >> 64) ^ t;
} // randu64
int n, par[Maxn];
int64_t w[Maxn], ans[Maxn];
vector<int> g[Maxn];
int sz[Maxn], son[Maxn];
int64_t sw[Maxn];
struct node {
  int ls, rs, size, id;
  uint64_t fix;
  int64_t val, add, mul;
  node() = default;
  node(int64_t val, int id) : ls(0), rs(0), size(1), fix(randu64()), val(val), id(id), add(0), mul(1) { }
} tr[Maxn];
int root, tot;
void initialize(void) { tr[root = tot = 0] = node(); }
int newnode(int64_t x, int id) { tr[++tot] = node(x, id); return tot; }
__attribute__((__always_inline__)) inline
void pushup(int p) { tr[p].size = tr[tr[p].ls].size + tr[tr[p].rs].size + 1; }
__attribute__((__always_inline__)) inline
void apply(int p, int64_t add, int64_t mul) {
  if (!p) return ;
  tr[p].val = tr[p].val * mul + add;
  tr[p].mul *= mul;
  tr[p].add = tr[p].add * mul + add;
} // apply
__attribute__((__always_inline__)) inline
void pushdown(int p) {
  apply(tr[p].ls, tr[p].add, tr[p].mul);
  apply(tr[p].rs, tr[p].add, tr[p].mul);
  tr[p].add = 0, tr[p].mul = 1LL;
} // pushdown
int join(int l, int r) {
  if (!l || !r) return l | r;
  pushdown(l), pushdown(r);
  if (tr[l].fix < tr[r].fix) {
    tr[l].rs = join(tr[l].rs, r);
    pushup(l); return l;
  } else {
    tr[r].ls = join(l, tr[r].ls);
    pushup(r); return r;
  }
} // join
void split(int p, int64_t key, int &l, int &r) {
  if (!p) return (l = r = 0), void(); pushdown(p);
  if (tr[p].val <= key) l = p, split(tr[p].rs, key, tr[l].rs, r), pushup(l);
  else r = p, split(tr[p].ls, key, l, tr[r].ls), pushup(r);
} // split
void insert(int64_t x, int i) {
  int A, B; split(root, x - 1, A, B);
  root = join(A, join(newnode(x, i), B));
} // insert
void modify(int64_t wl, int64_t wr, int64_t add, int64_t mul) {
  int A, B, C; split(root, wl - 1, A, B), split(B, wr, B, C);
  apply(B, add, mul); root = join(A, join(B, C));
} // modify
vector<pair<int64_t, int>> all;
void treap_dfs(int p) {
  if (!p) return; pushdown(p);
  treap_dfs(tr[p].ls);
  all.push_back({tr[p].val, tr[p].id});
  treap_dfs(tr[p].rs);
} // treap_dfs
vector<pair<int64_t, int>> traverse(int rt) {
  all.clear(); treap_dfs(rt); return all;
} // traverse
vector<pair<int64_t, int>> t[Maxn];
void sack_init(int u) {
  sz[u] = 1, sw[u] = w[u], son[u] = 0;
  for (const int &v: g[u]) {
    sack_init(v), sz[u] += sz[v], sw[u] += sw[v];
    if (son[u] == 0 || sz[v] > sz[son[u]]) son[u] = v;
  }
} // sack_init
void sack(int u) {
  for (const int &v: g[u]) if (v != son[u])
    sack(v), t[v] = traverse(root), initialize();
  vector<int64_t> ws, wall;
  for (const int &v: g[u]) wall.push_back(sw[v]);
  for (const int &v: g[u]) if (v != son[u]) ws.push_back(sw[v]);
  sort(ws.begin(), ws.end());
  sort(wall.begin(), wall.end());
  vector<int64_t> wsp = ws, wallp = wall;
  for (int i = 1; i < (int)ws.size(); ++i) wsp[i] += wsp[i - 1];
  for (int i = 1; i < (int)wall.size(); ++i) wallp[i] += wallp[i - 1];
  if (son[u] != 0) {
    sack(son[u]);
    if (!ws.empty()) {
      modify(ws.back(), inf, wsp.back(), 0 + 1);
      for (int i = (int)ws.size() - 2; i >= 0; --i)
        modify(ws[i], ws[i + 1] - 1, wsp[i], (int)ws.size() - i);
      modify(-inf, ws[0] - 1, 0LL, (int)ws.size() + 1);
    }
  }
  for (const int &v: g[u]) if (v != son[u]) {
    for (const auto &[x, i]: t[v]) {
      auto it = upper_bound(wall.begin(), wall.end(), x);
      int index = it - wall.begin() - 1;
      int64_t sum = (index >= 0 ? wallp[index] : 0), mul = (int)wall.size() - index;
      (it == wall.end() || sw[v] < *it) ? (sum -= sw[v]) : (mul--);
      insert(sum + mul * x, i);
    }
    t[v].clear();
  }
  insert(sw[u], u);
} // sack
int main(void) {
  scanf("%d", &n);
  for (int i = 1; i <= n; ++i) scanf("%d%lld", &par[i], &w[i]);
  for (int i = 1; i <= n; ++i) g[par[i]].push_back(i);
  sack_init(0); initialize(); sack(0);
  auto res = traverse(root);
  for (const auto &[x, i]: res) ans[i] = x;
  for (int i = 1; i <= n; ++i) printf("%lld\n", ans[i]);
  exit(EXIT_SUCCESS);
} // main
posted @ 2021-10-25 22:42  cutx64  阅读(78)  评论(0)    收藏  举报