CF1303G Sum of Prefix Sums

Posted libra9z

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF1303G Sum of Prefix Sums相关的知识,希望对你有一定的参考价值。

原题链接

这题好毒瘤啊!

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

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

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

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

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

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

(operatorname{ans}( ext x, ext y)=operatorname{ans}( ext v, ext y)+operatorname{ans}( ext x, ext u)+operatorname{len}( ext v, ext y)sumlimits_{win ext x ightarrow ext u}a_w)

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

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

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

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

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

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

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

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

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

查询时,就直接平衡树上二分每一条直线(l)是否满足最大值的要求,即(c)是否在(lcap 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
", ans);
  return 0;
}

以上是关于CF1303G Sum of Prefix Sums的主要内容,如果未能解决你的问题,请参考以下文章

CF1303G Sum of Prefix Sums[李超树,点分治]

CF1303G

CF1303G(点分治+李超线段树)

CF1149A Prefix Sum Primes

[题解] CF85D Sum of Medians

CF1006C Three Parts of the Array