CF1303G Sum of Prefix Sums

原题链接

这题好毒瘤啊!

首先看到是树上查询全局某式子的最大值,珂以想到这题无外乎是链分治(\(\text{dsu on tree}\))或点分治。作为一个专业的链分治选手, 窝在比赛中想了\(30\)min也不会。比赛后想了想,好像\(\text{dsu on tree}\)不星(珂能还是窝太菜了)。

点分治。因为是全局最大值,所以珂以不用在分治里容斥。

假设当前的子树的根是\(\text{u}\)

由于不用容斥,所以要确保在分治里面路径不能有重复边,这个珂以枚举\(\text{u}\)的儿子\(\text{v}\),然后枚举\(\text v\)为根的子树中的节点\(\text y\),另外开一个玩意儿维护之前的所有节点\(\text x\)的信息,其中\(\text x\)不在子树\(\text v\)里面。这样就珂以避免路径重复了。

因为询问是有方向的,为了方便,每一次枚举\(\{\text x,\text y\}\)时,默认方向为\(\text x\rightarrow\text u\rightarrow \text y\)。这样在枚举完\(\text u\)的儿子\(\text v\)之后,还要在反过来枚举一遍,即枚举另一个方向。

窝们考虑维护\(\text x\)的什么信息。设\(\text x\rightarrow \text y\)的答案为\(\operatorname{ans}(\text x,\text y)\)\(\text x\rightarrow\text y\)路径的长度为\(\operatorname{len}(\text x,\text y)\)

\(\operatorname{ans}(\text x,\text y)=\operatorname{ans}(\text v,\text y)+\operatorname{ans}(\text x,\text u)+\operatorname{len}(\text v,\text y)\sum\limits_{w\in \text x\rightarrow\text u}a_w\)

这里,\(\operatorname{ans}(\text v,\text y),\operatorname{len}(\text v,\text y)\)是定值。

所以这个式子就是一个\(a+x+by\)的结构,其中\(a,b\)是珂能会变的定值。

也就是说,窝们要搞一个数据结构,支持:

  1. 插入二元组\((x,y)\)
  2. 给定\(c\),查询\(\max\{x+cy\}\)

到这儿,想必神仙们都会用李超线段树来切掉这题了吧

然后由于窝这个菜鸡不会李超线段树,只能用其他方法。

和李超线段树做法一样,窝们把\((y,x)\)转化为平面上一条直线的\((k,b)\),然后查询就是询问\(\max\{f(c)\}\)

窝采用动态凸包,用平衡树维护哪些直线珂能成为最大值,按照\(k\)排序。

插入直线时,用叉积判断一条直线是否珂能成为最大值。

查询时,就直接平衡树上二分每一条直线\(l\)是否满足最大值的要求,即\(c\)是否在\(l\cap \operatorname{nxt}(l)\)左边。

放一下代码:

// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;

#ifndef LOCAL_JUDGE
static char _in_buf[100000], *_in_p1 = _in_buf, *_in_p2 = _in_buf;
#define gc (__builtin_expect(_in_p1 == _in_p2, 0) && (_in_p2 = (_in_p1 = _in_buf) + \
        fread(_in_buf, 1, 100000, stdin), _in_p1 == _in_p2) ? -1 : *_in_p1++)
#else
#define gc getchar()
#endif
inline int read() {
  register char ch = gc;
  register int x = 0;
  while (ch < 48 || ch > 57) ch = gc;
  while (ch > 47 && ch < 58) x = (x << 3) + (x << 1) + (ch ^ 48), ch = gc;
  return x;
}

static const int Maxn = 150005;

int n;
long long a[Maxn];
long long ans;
vector<int> g[Maxn];

// use to solve something that Li-Chao-Tree do
// add a line f(x), query max{f(a)}
namespace HULL {
  bool flag;
  struct line {
    long long k, b;
    mutable function<const line* ()> nxt;
    friend bool operator < (const line &a, const line &b) {
      if (!flag) return a.k < b.k;
      const line *s = a.nxt();
      if (!s) return false;
      return a.b - s->b < b.b * (s->k - a.k);
    }
  };
  struct dynamic_hull
  : public multiset<line> {
    // check if the line is maximum somewhere
    inline bool bad(iterator it) {
      if (it == this->end()) return false;
      auto nxt = next(it);
      if (it == this->begin()) {
        if (nxt == this->end()) return false;
        return it->k == nxt->k && it->b <= nxt->b;
      }
      auto prv = prev(it);
      if (nxt == this->end()) {
        return it->k == prv->k && it->b <= prv->b;
      }
      return (prv->b - it->b) * (nxt->k - it->k) >= (it->b - nxt->b) * (it->k - prv->k);
    }
    // add a new line to the current hull
    inline void add(long long k, long long b) {
      auto it = this->insert((line){k, b});
      it->nxt = [=]() { return next(it) == this->end() ? nullptr : &*next(it); };
      if (bad(it)) return void(this->erase(it));
      while (next(it) != this->end() && bad(next(it))) this->erase(next(it));
      while (it != this->begin() && bad(prev(it))) this->erase(prev(it));
    }
    // query the maximum value where <x> = x
    long long query(long long x) {
      if (this->empty()) return -1LL << 60;
      flag = true;
      line l = *lower_bound((line){0, x});
      flag = false;
      return l.k * x + l.b;
    }
  };
}
using HULL::dynamic_hull;

bool vis[Maxn];
int sz[Maxn], mxson[Maxn];
int root, total, mnson;

void get_root(int u, int fa) {
  sz[u] = 1, mxson[u] = 0;
  for (int v: g[u]) {
    if (v == fa || vis[v]) continue;
    get_root(v, u), sz[u] += sz[v];
    mxson[u] = max(mxson[u], sz[v]);
  }
  mxson[u] = max(mxson[u], total - sz[u]);
  if (mnson > mxson[u]) {
    root = u;
    mnson = mxson[u];
  }
}

int dep[Maxn];
long long dis[Maxn], d[Maxn];
dynamic_hull h;

void dfs_query(int u, int fa, int dep, long long dis, long long sum) {
  dis += a[u];
  sum += dis;
  ans = max(ans, h.query(dep) + sum);
  for (int &v: g[u]) {
    if (v == fa || vis[v]) continue;
    dfs_query(v, u, dep + 1, dis, sum);
  }
}
void dfs_modify(int u, int fa, int dep, long long dis, long long sum) {
  dis += a[u];
  sum += a[u] * dep;
  h.add(dis, sum);
  for (int &v: g[u]) {
    if (v == fa || vis[v]) continue;
    dfs_modify(v, u, dep + 1, dis, sum);
  }
}

void solve(int u) {
  ans = max(ans, a[u]);
  
  h.clear();
  for (int v: g[u]) {
    if (vis[v]) continue;
    dfs_query(v, 0, 1, 0LL, 0LL);
    dfs_modify(v, 0, 2, a[u], a[u]);
  }
}

void divide_root(int u) {
  vis[u] = true;
  
  solve(u);
  reverse(g[u].begin(), g[u].end());
  solve(u);
  
  for (int v: g[u]) {
    if (vis[v]) continue;
    
    total = sz[v], root = 0;
    mnson = 0x3f3f3f3f;
    get_root(v, 0);
    
    divide_root(root);
  }
}

int main() {
  n = read();
  for (int i = 1; i < n; ++i) {
    int u = read(), v = read();
    g[u].push_back(v);
    g[v].push_back(u);
  }
  for (int i = 1; i <= n; ++i) {
    a[i] = read();
  }
  
  total = n, root = 0;
  mnson = 0x3f3f3f3f;
  get_root(1, 0);
  
  divide_root(root);
  
  printf("%lld\n", ans);
  return 0;
}

原文地址:https://www.cnblogs.com/libra9z/p/12387308.html