AtCoder3611 Tree MST

题目描述
题解

不可能把完全图的边都找出来,需要考虑能否删去一些边使其与完全图的 mst \text{mst} mst 相同。

dis \text{dis} dis 我们可以考虑点分治,即对于一个点分中心,如果有三个点 x , y , z x,y,z x,y,z ,如果 W ( x , y ) ≤ W ( y , z ) W(x,y) \le W(y,z) W(x,y)W(y,z) 并且 W ( x , z ) ≤ W ( y , z ) W(x,z) \le W(y,z) W(x,z)W(y,z) ,那我们在做 mst \text{mst} mst 的时候肯定不会加上 W ( y , z ) W(y,z) W(y,z) 这一条边。

所以我们可以找到 W ( x , r t ) W(x,rt) W(x,rt) 最小的 x x x ,然后每个点都和 x x x 相连,这样得到的图的 mst \text{mst} mst 是等效于原来的完全图的,而这张图的边数是 O ( n l o g n ) O(nlogn) O(nlogn) 的,所以总效率为 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

代码
#include <bits/stdc++.h>
#define I inline
#define LL long long
using namespace std;
const int N=2e5+5;bool vis[N];LL G,s;
int n,m,X,f[N],sz[N],son[N],rt,o,hd[N],V[N*2],a[N],W[N*2],nx[N*2],t;
struct O{int u,v;LL w;}p[N*50];
int get(int x){return x==f[x]?x:f[x]=get(f[x]);}
I void add(int u,int v,int w){
	nx[++t]=hd[u];V[hd[u]=t]=v;W[t]=w;
}
#define v V[i]
I void getrt(int x,int fa){
	sz[x]=1;son[x]=0;
	for (int i=hd[x];i;i=nx[i])
		if (v!=fa && !vis[v])
			getrt(v,x),sz[x]+=sz[v],
			son[x]=max(son[x],sz[v]);
	son[x]=max(o-sz[x],son[x]);
	if (son[x]<son[rt]) rt=x;
}
I void find(int u,int fa,LL w){
	if (w+a[u]<G) G=w+a[u],X=u;
	for (int i=hd[u];i;i=nx[i])
		if (!vis[v] && v!=fa)
			find(v,u,w+W[i]);
}
I void ins(int u,int fa,LL w){
	p[++m]=(O){u,X,G+w+a[u]};
	for (int i=hd[u];i;i=nx[i])
		if (!vis[v] && v!=fa)
			ins(v,u,w+W[i]);
}
I void work(int x){
	vis[x]=1;G=2e18;X=0;
	find(x,0,0ll);ins(x,0,0ll);
	for (int i=hd[x];i;i=nx[i])
		if (!vis[v]) rt=0,o=sz[v],
			getrt(v,x),work(rt);
}
#undef v
bool cmp(O A,O B){return A.w<B.w;}
int main(){
	son[0]=1e9;
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]),f[i]=i;
	for (int x,y,z,i=1;i<n;i++)
		scanf("%d%d%d",&x,&y,&z),
		add(x,y,z),add(y,x,z);
	t=0;o=n;getrt(1,0);work(rt);
	sort(p+1,p+m+1,cmp);
	for (int u,v,i=1;i<=m;i++){
		u=get(p[i].u);v=get(p[i].v);
		if (u!=v) s+=p[i].w;f[u]=v;
	}
	cout<<s<<endl;return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值