Atcoder Code Festvial 2017 Final J Tree MST

版权声明:写得不好,转载请通知一声,还请注明出处,感激不尽 https://blog.csdn.net/As_A_Kid/article/details/88094354

Problem

Atcoder

给你一棵 n n 个节点的树,每个点有权值 w [ i ] w[i] ,边带权。现构建一张完全图,对于任意一对点 ( x , y ) (x, y) ,有一条长度为 w [ x ] + w [ y ] + d i s ( x , y ) w[x] + w[y]+ dis(x, y) 的边。求这张图的最小生成树。

话说Code Festival在官网上咋进啊?qwqq

Solution

队长:直觉告诉我这题是Boruvka算法
我:被神仙吊打.jpg

考虑Boruvka算法,我们就需要优化找到每个点最优选择的点的过程。不妨考虑点分治,设 s u m [ x ] sum[x] 表示 x x 到根的路径权值和,那么我们找到 w [ x ] + s u m [ x ] w[x]+sum[x] 最小的点,那么如果mst中有一条边跨越了当前根,这条边的一个端点一定是 x x ,把子树中的点都向 x x 连边即可。因此我们就把可能的边减少到了 O ( n log n ) O(n\log n) 级别。

等等,同一棵子树内的点对可能有问题?为了处理方便,我们可以放缩条件,把边权设为两点点权加上两点到根的权值,这样在同一棵子树中的边肯定不是最优的,我们只需要把这些可能的边最后做一次kruskal即可。

正确性:点分治第一层的连边就可以保证一定联通,而我们又把可能的边都存了起来,那么mst显然就已经被包括在这些边之中了。

时间复杂度 O ( n log 2 n ) O(n\log^2 n) ,空间复杂度 O ( n log n ) O(n\log n)


官方题解是这样的:直接用Boruvka算法,对于一个点,我们想要找到连向另一个联通块的边权最小的边。dp记录最优的边权和相应的联通块编号,dp两次即可得到每个点的最优选择。而每次联通块个数至少减半,因此算法的总复杂度为 O ( n log n ) O(n\log n)

Code

#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn=200010,INF=0x3f3f3f3f;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
struct Edge{
	int u,v;ll w;
	Edge(const int _u=0,const int _v=0,const ll _w=0ll){u=_u;v=_v;w=_w;}
	bool operator < (const Edge &b)const{return w<b.w;}
};
struct data{int v,w,nxt;}edge[maxn<<1];
int n,p,sn,rt,top,a[maxn],head[maxn],sz[maxn],mx[maxn],vis[maxn],stk[maxn];
ll ans,sum[maxn];
vector<Edge> G;
vector<Edge>::iterator itr;
struct Dsu{
	int f[maxn];
	void init(int n){for(int i=1;i<=n;i++) f[i]=i;}
	int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
	int merge(int x,int y)
	{
		int fx=find(x),fy=find(y);
		if(fx==fy) return 0;
		return f[fy]=fx;
	}
}dsu;
void insert(int u,int v,int w)
{
	edge[++p]=(data){v,w,head[u]};head[u]=p;
	edge[++p]=(data){u,w,head[v]};head[v]=p;
}
void getrt(int x,int pre)
{
	sz[x]=1;mx[x]=0;
	for(int i=head[x];i;i=edge[i].nxt)
	  if(!vis[edge[i].v]&&edge[i].v^pre)
	  {
	  	getrt(edge[i].v,x);
	  	sz[x]+=sz[edge[i].v];
	  	getmax(mx[x],sz[edge[i].v]);
	  }
	getmax(mx[x],sn-sz[x]);
	if(mx[x]<mx[rt]) rt=x;
}
void input()
{
	int u,v,w;
	read(n);
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<n;i++){read(u);read(v);read(w);insert(u,v,w);}
	dsu.init(n);
	sn=n;mx[rt=0]=INF;getrt(1,1);
}
void getdis(int x,int pre)
{
	stk[++top]=x;sz[x]=1;
	for(int i=head[x];i;i=edge[i].nxt)
	  if(!vis[edge[i].v]&&edge[i].v^pre)
	  {
	  	sum[edge[i].v]=sum[x]+edge[i].w;
	  	getdis(edge[i].v,x);
	  	sz[x]+=sz[edge[i].v];
	  }
}
void dfs(int x)
{
	int id;ll mn=1e18;
	vis[x]=1;top=0;sum[x]=0ll;
	getdis(x,x);
	for(int i=1;i<=top;i++)
	  if(getmin(mn,a[stk[i]]+sum[stk[i]]))
	    id=stk[i];
	for(int i=1;i<=top;i++)
	  G.push_back(Edge(id,stk[i],a[stk[i]]+sum[stk[i]]+a[id]+sum[id]));
	for(int i=head[x];i;i=edge[i].nxt)
	  if(!vis[edge[i].v])
	  {
	  	sn=sz[edge[i].v];rt=0;
	  	getrt(edge[i].v,x);
	  	dfs(rt);
	  }
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	#endif
	input();
	dfs(rt);
	sort(G.begin(),G.end());
	for(itr=G.begin();itr!=G.end();++itr)
	  if(dsu.merge(itr->u,itr->v))
	    ans+=itr->w;
	printf("%lld\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/As_A_Kid/article/details/88094354