题目描述
题解
不可能把完全图的边都找出来,需要考虑能否删去一些边使其与完全图的 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;
}