首页 > 其他分享 >AT_cf17_final_j Tree MST 题解

AT_cf17_final_j Tree MST 题解

时间:2023-12-10 16:12:51浏览次数:34  
标签:路径 int 题解 边权 MST Tree 最小 MAXN define

题意:给定一颗 \(n\) 个点的树,点 \(i\) 有权值 \(a_{i}\),边有边权。现在有另外一个完全图,两点之间的边权为树上两点之间的距离加上树上两点的点权,求这张完全图的最小生成树。

首先有一个很显然的暴力,把完全图中每两点之间的边权算出来,然后跑一边最小生成树,时间复杂度 \(O(n^{2} \log (n^{2}))\)。

考虑如何优化。发现有很多路径是不必要的,因为它们一定劣于其它路径,这些路径我们就不用加到完全图中去了。那么可以用点分治来筛选路径。

假设当前重心为 \(u\),我们可以把路径分为两种:

  • 一个端点是 \(u\) 的路径。

  • 经过 \(u\) 但是端点不在 \(u\) 的路径。

对于第一种路径,我们可以直接将它加入边集,因为总边数不超过 \(O(n \log n)\) 条。对于第二种,考虑如何选出最优的。假设两个点为 \(x\) 和 \(y\),那么可以把边权分为两个部分:\(x \to u\),\(u \to y\),即 \((a_{x}+dis(u,x))+(a_{y}+dis(u,y))\)。发现这个式子的前一半和后一半的形式是一样的,所以要让边权最小,只需要选一个 \((a_{x}+dis(u,x))\) 最小的 \(x\) 点,再连向其它所有的 \(y\) 点即可。

总时间复杂度 \(O(n \log^{2} n)\)。

本题的 Trick:求最小生成树遇到边数很多时,可以先把边权小的边拿出来,删除一些没用的边,然后再做最小生成树。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define inf 0x3f
#define inf_db 127
#define ls id << 1
#define rs id << 1 | 1
#define re register
#define endl '\n'
typedef pair <int,int> pii;
const int MAXN = 8e6 + 10;
int n,a[MAXN],head[MAXN],x,y,z,fa[MAXN],mn,id;
int pt,tot = 0,root,cnt,ans = 0;
bool vis[MAXN];
struct Node{int u,v,w,nxt;}e[MAXN << 1];
struct Edge{int u,v,w;}E[MAXN << 1];
struct F{int u,dis,idx;}p[MAXN << 1];
inline void Add(int u,int v,int w){e[++cnt] = {u,v,w,head[u]};head[u] = cnt;} 
inline int Get_size(int u,int father)
{
	if(vis[u] == true) return 0;
	int sum = 1;
	for(int i = head[u]; ~ i;i = e[i].nxt)
		if(e[i].v != father) sum += Get_size(e[i].v,u);
	return sum;
}
inline int Get_wc(int u,int father,int tot,int &wc)
{
	if(vis[u] == true) return 0;
	int sum = 1,mx = 0;
	for(int i = head[u]; ~ i;i = e[i].nxt)
	{
		int now = e[i].v;
		if(now == father) continue;
		int tmp = Get_wc(now,u,tot,wc);
		sum += tmp,mx = max(mx,tmp); 
	}
	if(max(mx,tot - sum) <= tot / 2) wc = u;
	return sum;
} 
inline void dfs(int u,int father,int dist,int r)
{
	if(vis[u] == true) return;
	int val = (a[u] + dist);
	p[++pt] = F{u,dist,r};
	if(val < mn) mn = val,id = u,root = r;
	for(int i = head[u]; ~ i;i = e[i].nxt)
	{
		int now = e[i].v;
		if(now == father) continue;
		dfs(now,u,dist + e[i].w,r);
	}
}
inline void solve(int u)
{
	if(vis[u] == true) return;
	Get_wc(u,0,Get_size(u,0),u),vis[u] = true;
	pt = 0,mn = 1e18,root = 0;
	for(int i = head[u]; ~ i;i = e[i].nxt)
	{
		int now = e[i].v;
		dfs(now,u,e[i].w,now);
	}
	for(int i = 1;i <= pt;i++)
		if(p[i].idx != root) E[++tot] = {id,p[i].u,mn + p[i].dis + a[p[i].u]};
	for(int i = 1;i <= pt;i++) E[++tot] = {u,p[i].u,p[i].dis + a[u] + a[p[i].u]};
	for(int i = head[u]; ~ i;i = e[i].nxt) solve(e[i].v); 
}
inline bool cmp(Edge x,Edge y){return x.w < y.w;}
inline int Find(int x)
{
	if(x == fa[x]) return x;
	return fa[x] = Find(fa[x]);
}
signed main()
{
	memset(head,-1,sizeof head);
	cin >> n;
	for(int i = 1;i <= n;i++) scanf("%lld",&a[i]);
	for(int i = 1;i < n;i++) scanf("%lld%lld%lld",&x,&y,&z),Add(x,y,z),Add(y,x,z);
	solve(1);
	sort(E + 1,E + tot + 1,cmp);
	for(int i = 1;i <= n;i++) fa[i] = i;
	for(int i = 1;i <= tot;i++)
		if(Find(E[i].u) != Find(E[i].v))
		{
			fa[Find(E[i].u)] = Find(E[i].v);
			ans += E[i].w;
		}
	cout << ans;
    return 0;
}



标签:路径,int,题解,边权,MST,Tree,最小,MAXN,define
From: https://www.cnblogs.com/Creeperl/p/17892750.html

相关文章

  • P7735 [NOI2021] 轻重边 题解
    是一道树剖好题,之前听lsl讲过一点,于是很快就做出来了。题意:有一个\(n\)个节点的树,最开始的时候所有边都是轻边,维护两个操作:操作一:将\(u\)到\(v\)的路径中经过的所有点的邻边变为轻边,再将这条路径上的边变为重边。操作二:求出\(u\)到\(v\)这条路径上有多少条重边......
  • CodeForces 1902F Trees and XOR Queries Again
    洛谷传送门CF传送门如果我们能把\(x\toy\)路径上的所有点权插入到线性基,那么可以\(O(\logV)\)查询。但是因为线性基合并只能\(O(\log^2V)\)(把一个线性基的所有元素插入到另一个),所以只能倍增做\(O((n+q)\logn\log^2V)\),过不了。考虑\(O(n\logV)\)预处理出......
  • 小程序建立用户与数据的联系问题解决方案
    在小程序中建立用户与数据的联系是一个常见的问题,在本文中提供了一个解决方案。这个解决方案包括几个关键步骤。首先,需要通过用户登录功能实现用户的身份识别,并获取到用户的唯一标识符。接着,需要在后台数据库中创建一个用户表,用于存储用户的基本信息和与之相关联的数据。在这个表中......
  • ARC169 B Subsegments with Small Sums 题解
    LinkARC169BSubsegmentswithSmallSumsQuestion\(x\)是一个序列,定义\(f(x)\)为把序列\(x\)切成几段,每段的和不能超过\(S\)的最小段数给出序列\(A=(A_1,A_2,\cdots,A_N)\)求:\[\sum_{1\lel\leN}f((A_l,A_{l+1},\cdots,A_r))\]Question先考虑一个结论,\(x\)为......
  • P9915 「RiOI-03」3-2 题解
    更好的阅读这是一道找规律的题目。因为我个人习惯,以下部分使用从\(1\)开始的下标讲述。首先我们以\(1\)来说:发现在第\(x\)行\(y\)列的连通块是可以直接连到第\(1\)列的,所以很容易可以得出\(1\)到\(y\)列的连通块数量是\(2^y-1\)。接着,我们考虑再后面的情况:......
  • Linux中的红黑树(rbtree)【ChatGPT】
    https://www.kernel.org/doc/html/v6.6/core-api/rbtree.html红黑树(rbtree)在Linux中日期2007年1月18日作者[email protected]红黑树是什么,它们有什么作用?红黑树是一种自平衡的二叉搜索树,用于存储可排序的键/值数据对。这与基数树(用于高效存储稀疏数组,因......
  • 常见问题解决 --- pip SSLEOFError
    问题C:\Users\Administrator\Desktop>pipinstallscapy-ihttp://pypi.douban.com/simple--trusted-hostpypi.douban.comLookinginindexes:http://pypi.douban.com/simpleWARNING:Retrying(Retry(total=4,connect=None,read=None,redirect=None,status=None......
  • 【题解】CQOI2017 - 小 Q 的表格
    【题解】CQOI2017-小Q的表格https://www.luogu.com.cn/problem/P3700首先考虑题面给的两个式子。由式二可以得到:\[\dfrac{f(a,a+b)}{a(a+b)}=\dfrac{f(a,b)}{ab}\]发现这个很像辗转相除,可得\[\dfrac{f(a,b)}{ab}=\dfrac{f(a,a\bmodb)}{a(a\bmodb)}\]然后由式一转换,最......
  • 【JavaSE】集合Collection{List(ArrayList, LinkedList), Set(TreeSet, HashSet, Link
    集合单列集合:Collection接口单列集合:一次添加一个元素;如果集合中添加的是类,要重写equals方法,否则比较的是地址,无法正常删除内容相同的元素。单列集合通用遍历方式1.迭代器遍历2.增强for循环遍历增强for循环底层逻辑还是迭代器,字节码文件反编译为java会发现还是迭代......
  • CF1773J King's Puzzle 题解
    题意:思路:当$k\gen$时,一定无法构造。证明:$n$个点的无向图,每个点的度数$d∈[1,n-1]$,度数的种数一定不会超过$n-1$。当$k\len-1$时,构造方案如下:首先,选取前$k+1$个点,构造成一条链,此时链上各点的度数为$1$,$2$,$2$,$...$,$2......