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)是珂能会变的定值。
也就是说,窝们要搞一个数据结构,支持:
- 插入二元组((x,y))
- 给定(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的主要内容,如果未能解决你的问题,请参考以下文章