Solution
H(x)=∑|x−wu|
这是一个凸包。并且在相近的两个点对(可以有重复)斜率相差
1
。 考虑如何从子树加入一条连向父亲的边,形成新的凸包
设取到最小值的区间为 [L,R] 。
H′(x)=⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪H(x)+wH(L)+w−(x−L)H(L)H(L)+(x−R)−wx≤LL≤x≤L+wL+w≤x≤R+wR+w≤x
这其实是把 L 左边的一部分平移,加入
用 这里的方法可以维护这个东西,这道题因为要合并不同子树的拐点,所以就用可并堆啦~
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 606060;
inline char get(void) {
static char buf[100000], *S = buf, *T = buf;
if (S == T) {
T = (S = buf) + fread(buf, 1, 100000, stdin);
if (S == T) return EOF;
}
return *S++;
}
template<typename T>
inline void read(T &x) {
static char c; x = 0; int sgn = 0;
for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
if (sgn) x = -x;
}
struct node {
node *l, *r;
ll v;
node(ll _v = 0): v(_v), l(NULL), r(NULL) {}
};
node mem[N];
node *rt[N];
int n, m, mnt, pnt;
ll sum;
int fa[N], w[N], deg[N];
inline int Rand(void) {
static int x = 31253125;
x += (x << 4) + 1;
return x & 65536;
}
inline node *Merge(node *x, node *y) {
if (!x || !y) return x ? x : y;
if (x->v < y->v) swap(x, y);
if (Rand()) x->l = Merge(x->l, y);
else x->r = Merge(x->r, y);
return x;
}
inline void Pop(node *&x) {
x = Merge(x->l, x->r);
}
int main(void) {
freopen("1.in", "r", stdin);
read(n); read(m);
for (int i = 2; i <= n + m; i++) {
read(fa[i]); read(w[i]);
sum += w[i]; ++deg[fa[i]];
}
for (int i = n + m; i > 1; i--) {
ll l = 0, r = 0;
if (i <= n) {
while (--deg[i]) Pop(rt[i]);
r = rt[i]->v; Pop(rt[i]);
l = rt[i]->v; Pop(rt[i]);
}
mem[++mnt] = node(l + w[i]);
mem[++mnt] = node(r + w[i]);
rt[i] = Merge(rt[i], mem + mnt - 1);
rt[i] = Merge(rt[i], mem + mnt);
rt[fa[i]] = Merge(rt[fa[i]], rt[i]);
}
while (deg[1]--) Pop(rt[1]);
while (rt[1]) {
sum -= rt[1]->v; Pop(rt[1]);
}
printf("%lld\n", sum);
return 0;
}