一道有趣的题 [Kruskal重构树]

题目摘自https://blog.csdn.net/hwzzyr/article/details/81190442

路径权值 

给定一个带权树,树上任意两点间的路径权值d(x,y)定义为x,y这两个点之间路径上的最小值,树上任意一点x的权值定义为这个点到树上其他所有点的路径权值和,即  \sum_{i=1}^nd(x,i) ,现求树上一点,使得这个点的权值最大,输出这个值。 


一般路径最小/最大可以转到重构树上 , 这样路径最小/最大就是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;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值