原题链接
这题好毒瘤啊!
首先看到是树上查询全局某式子的最大值,珂以想到这题无外乎是链分治(\(\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\)是珂能会变的定值。
也就是说,窝们要搞一个数据结构,支持:
- 插入二元组\((x,y)\)
- 给定\(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