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

题意:

选出一条链 \([u \to v]\),每个点有个权值,然后求 \(\max{\sum s_i}\)\(s_i\) 为路径前缀和。

首先路径问题先想到点分治,然后考虑如何链上计算这个贡献,如果当前重心为 \(x\),我们选出了两个点 \(u,v\) ,非常显然 \(u,v\) 都是叶子结点。
(证明略)

然后一部分的贡献是 \([u \to x]\),另一部分怎么算呢?(指 \([x \to v]\)),我们先忽略左边的长度给他带来的贡献,把本身贡献计算一下,然后很显然,这个可以把 \(\sum a_i [i \in [x \to v]]\) 记下来,然后就变成了一个 \(kx+b\) 的形式,就可以李超树/动态凸包了。

也就是 \(k \times \sum a_i [x \to v] + \sum s_i [x \to v] + \sum s_i[u \to x]\)

然后枚举即可。

由于我菜的可怜不会动态凸包,所以写了个李超树。

#include <bits/stdc++.h> 
using namespace std;
#define int long long
int read() {
	int x = 0; char c = getchar();
	while(!isdigit(c)) c = getchar();
	while(isdigit(c)) x = x * 10 + (c - 48), c = getchar();
	return x;
}
int n;
const int maxn = 2e5 + 52;
int a[maxn], deg[maxn];
vector <int> g[maxn];
struct Line {
	int k, b; Line(int K = 0, int B = -1e18) : k(K), b(B) {}
	inline int val(int x) { return k * x + b; }
} t[maxn << 2];
void build(int l, int r, int p) {
	t[p] = Line(); if(l == r) return;
	int mid = l + r >> 1; build(l, mid, p << 1); build(mid + 1, r, p << 1 | 1);
}
void mdf(int l, int r, int p, Line x) {
	int lx = x.val(l), ly = t[p].val(l), rx = x.val(r), ry = t[p].val(r);
	if(lx <= ly && rx <= ry) return;
	if(lx >= ly && rx >= ry) { t[p] = x; return; }
	int mid = l + r >> 1, midx = x.val(mid), midy = t[p].val(mid);
	if(midx > midy) swap(t[p], x); x.k < t[p].k ? mdf(l, mid, p << 1, x) : mdf(mid + 1, r, p << 1 | 1, x); 
}
int qry(int l, int r, int p, int x) {
	int ans = t[p].val(x), mid = l + r >> 1;
	if(l == r) return ans;
	return max(ans, x <= mid ? qry(l, mid, p << 1, x) : qry(mid + 1, r, p << 1 | 1, x));
}
int fr[maxn], pos[maxn], tp = 0, upk[maxn], upb[maxn], dwn[maxn];
void add(int f, int sum ,int up, int dw, int d) { fr[++ tp] = f; upk[tp] = sum;  upb[tp] = up; dwn[tp] = dw; pos[tp] = d; }
int rt, tot, mxrt, vis[maxn], sz[maxn], dep[maxn];
void frt(int u, int p) {
	int mxs = 0; sz[u] = 1;
	for(int v: g[u]) if(!vis[v] && v ^ p) { frt(v, u); sz[u] += sz[v]; mxs = max(mxs, sz[v]); }
	mxs = max(mxs, tot - sz[u]); if(mxs < mxrt) { rt = u; mxrt = mxs; }
}
int leaf[maxn];
void DFS(int u, int p, int f, int sum, int up, int dw) {
	dep[u] = dep[p] + 1; if(u != rt && !f) { f = u; } if(deg[u] == 1) add(f, sum - a[rt], up, dw, dep[u]), leaf[u] = 1;
	for(int v: g[u]) if(!vis[v] && v ^ p) DFS(v, u, f, sum + a[v], up + dep[u] * a[v], dw + sum + a[v]), leaf[u] += leaf[v];
}
int ans = 0;
void solve(int u) {
	tp = 0; DFS(u, 0, 0, a[u], 0, a[u]); int mxd = *max_element(pos + 1, pos + tp + 1);
	build(1, mxd, 1);
	for(int i = 1, j; i <= tp; i = j) {
		for(j = i; j <= tp && fr[j] == fr[i]; ++ j) ans = max(ans, qry(1, mxd, 1, pos[j]) + dwn[j]);
		for(j = i; j <= tp && fr[j] == fr[i]; ++ j) mdf(1, mxd, 1, Line(upk[j], upb[j]));
	}
	build(1, mxd, 1);
	for(int i = tp, j; i >= 1; i = j) {
		for(j = i; j >= 1 && fr[j] == fr[i]; -- j) ans = max(ans, qry(1, mxd, 1, pos[j]) + dwn[j]);
		for(j = i; j >= 1 && fr[j] == fr[i]; -- j) mdf(1, mxd, 1, Line(upk[j], upb[j]));
	}
	vis[u] = 1; int osz = tot;
	for(int v: g[u]) if(!vis[v] && leaf[v] >= 2) { mxrt = tot = sz[v] < sz[u] ? sz[v] : osz - sz[u]; frt(v, u), solve(rt); }
}
#define pb push_back
signed main() {
	n = read();
	for(int i = 1 ; i < n ; i ++) { int u = read(), v = read(); g[u].pb(v), g[v].pb(u), deg[u]++, deg[v]++; }
	for(int i = 1 ; i <= n ; i ++) a[i] = read(); tot = mxrt = n; frt(1, 0), solve(rt);
	cout << ans << '\n';
	return 0;
}
posted @ 2020-05-06 20:51  _Isaunoya  阅读(165)  评论(0编辑  收藏  举报