版权声明:写得不好,转载请通知一声,还请注明出处,感激不尽 https://blog.csdn.net/As_A_Kid/article/details/88094354
Problem
给你一棵 个节点的树,每个点有权值 ,边带权。现构建一张完全图,对于任意一对点 ,有一条长度为 的边。求这张图的最小生成树。
话说Code Festival在官网上咋进啊?qwqq
Solution
队长:直觉告诉我这题是Boruvka算法
我:被神仙吊打.jpg
考虑Boruvka算法,我们就需要优化找到每个点最优选择的点的过程。不妨考虑点分治,设 表示 到根的路径权值和,那么我们找到 最小的点,那么如果mst中有一条边跨越了当前根,这条边的一个端点一定是 ,把子树中的点都向 连边即可。因此我们就把可能的边减少到了 级别。
等等,同一棵子树内的点对可能有问题?为了处理方便,我们可以放缩条件,把边权设为两点点权加上两点到根的权值,这样在同一棵子树中的边肯定不是最优的,我们只需要把这些可能的边最后做一次kruskal即可。
正确性:点分治第一层的连边就可以保证一定联通,而我们又把可能的边都存了起来,那么mst显然就已经被包括在这些边之中了。
时间复杂度 ,空间复杂度 。
官方题解是这样的:直接用Boruvka算法,对于一个点,我们想要找到连向另一个联通块的边权最小的边。dp记录最优的边权和相应的联通块编号,dp两次即可得到每个点的最优选择。而每次联通块个数至少减半,因此算法的总复杂度为 。
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;
}