题意一棵树,两点的路径和d(u,v)为相邻边权差的平方和的总和。对于每个点,求maxvd(u,v)maxvd(u,v)
思路 经典树型DP+斜率优化
我们考虑每个点向上走与向下走能获得的答案,那么每个点总的答案就是max(up[i],down[i])max(up[i],down[i]) 向下走的答案是容易求得的,只需要从叶子向上DP即可。向上走的答案就要麻烦一些了。对于一个点
up[i]=max(up[ifather]+(c[i]−c[ifather])2,down[ibrother]+(c[i]−c[ibrother])2)up[i]=max(up[ifather]+(c[i]−c[ifather])2,down[ibrother]+(c[i]−c[ibrother])2)
这里出现了二次项,是一个经典的斜率优化DP。考虑
max{down[ibrother]+(c[i]−c[ibrother])2}max{down[ibrother]+(c[i]−c[ibrother])2}
把这个式子拆开来, 我们要从j转移而不是从k转移的条件是
(down[j]+c[j]2)−(down[k]+c[k]2)>2∗c[i]∗(c[j]−c[k])(down[j]+c[j]2)−(down[k]+c[k]2)>2∗c[i]∗(c[j]−c[k])
对于每个状态,将其看作一个point(c[j],c[j]2+down[j])point(c[j],c[j]2+down[j]) 则该式子可看作c[i]c[i] <= pjpj与pkpk 两点的斜率(c[j]>c[k]c[j]>c[k]的情况下), up[i]将从j转移过来。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
const int N = 1e5 + 10;
ll sqr(ll x){
return x*x;
}
struct point{
ll x,y;
int id;
bool operator < (point &b){
return x < b.x;
}
point operator - (point &p){
return {x - p.x, y - p.y};
}
};
ll dot(point a, point b) {
return a.x * b.x + a.y * b.y;
}
ll cross(point a, point b) {
return a.x * b.y - a.y * b.x;
}
vector<point>G[N];
ll down[N], up[N], ans[N], c[N];
void dfs1(int u, int fa){
for (point e : G[u]){
int v = e.x, w = e.y;
if (v == fa) continue;
c[v] = w;
dfs1(v, u);
if (fa) {
down[u] = max(down[u], down[v] + sqr(c[v] - c[u]));
ans[fa] = max(ans[fa], down[u]);
}
}
}
point ch[N], son[N];
int top, m;
void dfs2(int u, int fa){
m = 0;
for (point e : G[u]){
int v = e.x;
if (v == fa) continue;
if (fa) up[v] = max(up[v], up[u] + sqr(c[v] - c[u]));
son[m++] = {c[v], down[v] + sqr(c[v]), v};
}
sort(son, son + m);
top=0;
for (int i = 0; i < m; i++){
int id = son[i].id;
point vec = {-2 * c[id], 1};
while(top > 1 && dot(vec, ch[top - 1]) <= dot(vec, ch[top - 2])) top--;
if (top) up[id] = max(up[id], dot(vec, ch[top - 1]) + sqr(c[id]));
while(top > 1 && cross(ch[top - 1] - ch[top - 2], son[i] - ch[top - 2]) >= 0) top--;
ch[top++] = son[i];
}
top = 0;
for (int i = m - 1; i >= 0; i--){
int id = son[i].id;
point vec={-2 * c[id], 1};
while(top > 1 && dot(vec, ch[top - 1]) <= dot(vec, ch[top - 2])) top--;
if (top) up[id] = max(up[id], dot(vec, ch[top - 1]) + sqr(c[id]));
while(top > 1 && cross(ch[top - 1] - ch[top - 2], son[i] - ch[top - 2]) <= 0) top--;
ch[top++] = son[i];
}
for (point e : G[u]){
int v = e.x;
if (v == fa) continue;
dfs2(v, u);
}
}
int main(){
int n;
while(~scanf("%d", &n)){
for (int i = 1;i <= n; i++) {
G[i].clear();
ans[i] = down[i] = up[i] = c[i] = 0;
}
for (int i = 1, x, y, z; i < n; i++){
scanf("%d%d%d", &x, &y, &z);
G[x].push_back({y, z});
G[y].push_back({x, z});
}
dfs1(1, 0);
dfs2(1, 0);
for (int i = 1;i <= n; i++) printf("%lld\n", max(ans[i], up[i]));
}
}