• 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;
    }
    
    
  • 相关阅读:
    算法提高 道路和航路
    奇偶剪枝
    二分求值
    并查集--路径压缩
    Oracle数据库导入导出DMP文件
    Spring IoC的实现与思考(一)
    sql基础拾遗
    jquery事件函数的使用之focus
    Java动态代理之cglib
    Java se之动态代理
  • 原文地址:https://www.cnblogs.com/libra9z/p/12387308.html
Copyright © 2020-2023  润新知