题目摘自https://blog.csdn.net/hwzzyr/article/details/81190442
路径权值
给定一个带权树,树上任意两点间的路径权值d(x,y)定义为x,y这两个点之间路径上的最小值,树上任意一点x的权值定义为这个点到树上其他所有点的路径权值和,即 ,现求树上一点,使得这个点的权值最大,输出这个值。
一般路径最小/最大可以转到重构树上 , 这样路径最小/最大就是lca的值 , 比路径好维护
我们重构出树后 , 考虑一个点的贡献 , 发现一个点的左儿子到右儿子比经过那个点
于是左儿子对右儿子的贡献就是 val[x] * siz[ls] , 把右儿子的区间区间加 , 最后单点查询某个点的值取最大的
区间修改单点查询用树状数组
#include<bits/stdc++.h>
#define N 200050
using namespace std;
struct Node{int u,v,w;}E[N];
bool cmp(Node a,Node b){return a.w>b.w;}
int first[N],next[N],to[N],tot;
void add(int x,int y){next[++tot]=first[x],first[x]=tot,to[tot]=y;}
int n,cnt,ans,val[N],st[N],ed[N],siz[N],sign;
int fa[N]; int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int c[N];
void Up(int x,int val){for(;x<=cnt;x+=x&-x) c[x]+=val;}
int Quary(int x){int ans=0; for(;x;x-=x&-x) ans += c[x]; return ans;}
void Kruskal(){
sort(E+1,E+n,cmp); cnt = n;
for(int i=1;i<n;i++){
int x = E[i].u, y = E[i].v;
int fx = find(x), fy = find(y);
if(fx!=fy){
val[++cnt] = E[i].w;
fa[fx] = fa[fy] = cnt;
add(cnt,fx), add(cnt,fy);
}
}
}
void dfs(int u){
if(u<=n) siz[u] = 1;
st[u] = ++sign;
for(int i=first[u];i;i=next[i]){
int t=to[i];
dfs(t); siz[u] += siz[t];
} ed[u] = sign;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n*2;i++) fa[i] = i;
for(int i=1;i<n;i++){
scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].w);
} Kruskal(); dfs(cnt);
for(int i=n+1;i<=cnt;i++){
int ls=0, rs=0;
for(int j=first[i];j;j=next[j]){
if(ls) rs = to[j];
else ls = to[j];
}
Up(st[ls], val[i] * siz[rs]); Up(ed[ls]+1, -val[i] * siz[rs]);
Up(st[rs], val[i] * siz[ls]); Up(ed[rs]+1, -val[i] * siz[ls]);
}
for(int i=1;i<=n;i++) ans = max(ans, Quary(st[i]));
printf("%d",ans); return 0;
}