AT3611 Tree MST

一、题目

点此看题

二、解法

我自己做的时候用 \(\tt Boruvka\) 加点分治,一直 \(\tt T\) 三个点根本卡不过去。

其实可以直接点分治,因为我们只要考虑了每条路径就考虑了每条边,那么对于每个分治中心,我们求出子树内到中心点的距离 \(dis[i]\),记 \(p[i]=w[i]+dis[i]\),找到 \(p[i]\) 最小的点和每个点配对即可,不需要考虑在同一个子树内的情况,因为这种边一定不优。

那么我们通过点分治得到了 \(O(n\log n)\) 条边,跑 \(\tt kruskall\) 就可以做到 \(O(n\log^2n)\)

如果要用 \(\tt Boruvka\) 也可以,每次跑两遍 \(dp\) 处理出最优匹配点即可,时间复杂度 \(O(n\log n)\)

三、总结

最小生成树还有一种重要的思路是保留有用的边。

路径问题考虑点分治。

#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 200005;
#define int long long
const int inf = 1e18;
#define pii pair<int,int>
#define mp make_pair
#define x first
#define y second
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,sz,tot,ans,f[M];pii rt,mi;
int fa[M],siz[M],w[M],dis[M],vis[M];
struct edge
{
	int v,c,next;
}e[2*M];
struct node
{
	int u,v,c;
	bool operator < (const node &b) const
	{
		return c<b.c;
	}
};vector<node> v;vector<int> nw;
void findrt(int u,int fa)
{
	siz[u]=1;int mx=0;
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(vis[v] || v==fa) continue;
		findrt(v,u);
		siz[u]+=siz[v];
		mx=max(mx,siz[v]);
	}
	mx=max(mx,sz-siz[u]);
	rt=min(rt,mp(mx,u));
}
void dfs(int u,int fa)
{
	mi=min(mi,mp(dis[u]+w[u],u));
	nw.push_back(u);
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==fa || vis[v]) continue;
		dis[v]=dis[u]+e[i].c;
		dfs(v,u);
	}
}
void solve(int u)
{
	vis[u]=1;dis[u]=0;mi=mp(inf,0);
	nw.clear();dfs(u,u);
	for(auto x:nw)
		v.push_back(node{x,mi.y,mi.x+dis[x]+w[x]});
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(vis[v]) continue;
		rt=mp(n,0);sz=siz[v];
		findrt(v,0);solve(rt.y);
	}
}
int find(int x)
{
	if(fa[x]!=x) fa[x]=find(fa[x]);
	return fa[x];
}
void kruskall()
{
	sort(v.begin(),v.end());
	for(int i=1;i<=n;i++) fa[i]=i;
	for(auto t:v)
	{
		int x=find(t.u),y=find(t.v);
		if(x==y) continue;
		fa[x]=y;ans+=t.c;
	}
}
signed main()
{
	freopen("zxy.in","r",stdin);
	n=read();
	for(int i=1;i<=n;i++)
		w[i]=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read(),c=read();
		e[++tot]=edge{v,c,f[u]},f[u]=tot;
		e[++tot]=edge{u,c,f[v]},f[v]=tot;
	}
	sz=n;rt=mp(n,0);
	findrt(1,0);solve(rt.y);
	kruskall();
	printf("%lld\n",ans);
}
posted @ 2021-10-05 16:31  C202044zxy  阅读(72)  评论(0编辑  收藏  举报