当前位置: 首页 > article >正文

数据结构选讲 (更新中)

参考 smWCDay7 数据结构选讲2 by yyc

可能会补充的:

  • AT_cf17_final_j TreeMSTF2 Boruvka算法

目录

  • AT_cf17_final_j Tree MST

AT_cf17_final_j Tree MST

link
题意
给定一棵 n n n 个点的树,点有点权 w i w_i wi,边有边权。建立一张完全图 G G G,节点 u , v u, v u,v 之间的边长为 w u + w v + d i s ( u , v ) w_u + w_v + dis(u, v) wu+wv+dis(u,v) 。求 G G G M S T MST MST (最小生成树)的边权和。

  • n ≤ 2 × 1 0 5 , 1 ≤ w i ≤ 1 0 9 n \le 2 \times 10^5 ,1 \le w_i \le 10^9 n2×1051wi109

思路
前置指示:点分治

引理:边 e e e 在边集 E E E 的最小生成树中,则对于任意 E 0 ⊆ E E_0 ⊆ E E0E,e 也在边集 E 0 E_0 E0 的最小生成树中。
证明:考虑 kruskal
推论:将边分为若干组,分别求最小生成树,再合并求最小生成树,结果正确。

题目是说建立一张完全图然后求其的 M S T MST MST ,但是这样的边是 n 2 n^2 n2 级别的,考虑去掉一些多余的边,只保留有用的边。所以可以将完全图 G G G 分成很多个边集,分别求 M S T MST MST (不必强制联统),然后保留有用边,最后再综合求 M S T MST MST 就是答案了。
考虑 点分治,以重心为根,将树分成几个子树,那么就只用处理跨越重心的边即可。
d i s i dis_i disi 表示 点 i i i 到重心的距离,跨越重心的边 ( u , v ) (u,v) (u,v),边权是 w u + d i s u + w v + d i s v w_u + dis_u + w_v + dis_v wu+disu+wv+disv 。由于每个点 i i i 都要贡献至少一次 w i + d i s i w_i+dis_i wi+disi,另外一边肯定选最小的 w j + d i s j w_j+dis_j wj+disj ,所以我们只需要选择 w i + d i s i w_i + dis_i wi+disi 最小的点,让它和所有其他点连边,该菊花图就是最小生成树。(在同一棵子树内连了边也不影响结果,因为它们的边权比真实值要大 d i s u + d i s v > d i s u , v dis_u + dis_v \gt dis_{u,v} disu+disv>disu,v )。这样边的数量就减到了 n l o g n nlogn nlogn,总时间复杂度为 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

好像还有一种方法,要用到 Boruvka算法 ,后续可能会学……

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5;

int idx,head[maxn];
struct EDGE{ int v,next; ll w; }e[maxn*2];
void Insert(int u,int v,ll w){
	e[++idx]={v,head[u],w};
	head[u]=idx;
}

int siz[maxn],mxson[maxn],pot[maxn],tn;
bool vis[maxn];
void Dfs(int x,int fa){
	pot[++tn]=x,siz[x]=1,mxson[x]=0;
	for(int i=head[x];i;i=e[i].next){
		int v=e[i].v;
		if(!vis[v]&&v!=fa){
			Dfs(v,x);
			siz[x]+=siz[v];
			mxson[x]=max(mxson[x],siz[v]);
		}
	}
}

int Get_root(int x){
	tn=0,Dfs(x,0);
	int root=0;
	for(int i=1;i<=tn;i++){
		mxson[pot[i]]=max(mxson[pot[i]],tn-siz[pot[i]]);
		if(!root||mxson[root]>mxson[pot[i]]) root=pot[i]; 
	}
	return root;
}

int id;
ll a[maxn],dis[maxn];
void Dfs2(int x,int fa){
	for(int i=head[x];i;i=e[i].next){
		int v=e[i].v;
		if(!vis[v]&&v!=fa){
			dis[v]=dis[x]+e[i].w;
			Dfs2(v,x);
		}
	}
	if(!id||a[x]+dis[x]<a[id]+dis[id]) id=x;
}

int cnt;
struct EDGE2{ int u,v; ll w; }te[maxn*20];
void Solve(int rt){
	dis[rt]=0,id=0,Dfs2(rt,0);
	for(int i=1;i<=tn;i++)
		te[++cnt]={id,pot[i],a[id]+dis[id]+a[pot[i]]+dis[pot[i]]};
	vis[rt]=1;
	for(int i=head[rt];i;i=e[i].next){
		int v=e[i].v;
		if(!vis[v]) Solve(Get_root(v));
	}
}

int fa[maxn];
int Find_fa(int x){ return fa[x]==x?x:fa[x]=Find_fa(fa[x]); }

int main(){
	int n; cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=1;i<n;i++){
		int x,y,z; cin>>x>>y>>z;
		Insert(x,y,z),Insert(y,x,z);
	}
	Solve(Get_root(1));
	sort(te+1,te+cnt+1,[](EDGE2 a,EDGE2 b){ return a.w<b.w; });
	for(int i=1;i<=n;i++)
		fa[i]=i;
	ll ans=0;
	for(int i=1;i<=cnt;i++){
		int fx=Find_fa(te[i].u),fy=Find_fa(te[i].v);
		if(fx!=fy) fa[fy]=fx,ans+=te[i].w;
	}
	cout<<ans;
	return 0;
}

http://www.kler.cn/a/524478.html

相关文章:

  • 快速分析LabVIEW主要特征进行判断
  • 16、智能驾驶域控的材料回收
  • 2025年美赛B题-结合Logistic阻滞增长模型和SIR传染病模型研究旅游可持续性-成品论文
  • Linux 4.19内核中的内存管理:x86_64架构下的实现与源码解析
  • RubyFPV开源代码之系统简介
  • JJJ:linux时间子系统相关术语
  • 2859.计算K置位下标对应元素的和
  • docker镜像拉取失败超时
  • PTA乙级1006~1010【c++】
  • AI学习指南HuggingFace篇-Hugging Face 简介与背景
  • Java实现LRU缓存策略实战
  • 如何解决TikTok网络不稳定的问题
  • GMSL 明星产品之 MAX96717
  • SQL99之内连接查询
  • 前端【11】HTML+CSS+jQUery实战项目--实现一个简单的todolist
  • 深度学习学习笔记(第31周)
  • 线段树 算法
  • PA1记录
  • TiDB 常用命令
  • Java---入门基础篇(上)
  • vue-有关于TS与路由器
  • android wifi 热点名称的默认配置
  • 企业SaaS(软件即服务)行业中AARRR
  • 搭建Spark分布式集群
  • 学习数据结构(2)空间复杂度+顺序表
  • 昆仑万维Java开发面试题及参考答案