感觉是一道典中典题目!
首先发现一个性质,假设 ( x , y ) (x,y) (x,y)在最终的答案当中,那么对于 x → y x\to y x→y这条链上的任意节点 z z z,应该满足 w z ≥ min ( w x , w y ) w_z\ge \min(w_x,w_y) wz≥min(wx,wy)。
这样我们每次把权值最小的点取出来作为根,然后把所有可能有用的边找出来,最后跑 kruskal \text{kruskal} kruskal算法,但是复杂度可能不对!
我们发现这样一个类似于 点分树 的结构有非常好的性质,于是问题转化为求一个子树内 d i s x + w x dis_x+w_x disx+wx的最小值。
但是这样是有问题的。我们想一想一般的点分树能否实现上述功能?
发现这样是可以的。具体来说只要把所有点和 d i s x + w x dis_x+w_x disx+wx最小的那个点连起来就好了,因为两个点之间边的边权就是点权相加,因此所有点都只可能会和点权最小的那个点连边。
但是就是没想到\kk。以为跨分治中心的边不好处理所以就卡住了。。。
复杂度 O ( n log 2 n ) O(n\log^2 n) O(nlog2n)。
remark \text{remark} remark 为什么这道题目可以用点分治解决?因为和路径相关!但是我以为最开始那个结论很重要。。。还是太菜了。。。
#include
#define fi first
#define se second
#define ll long long
#define pb push_back
#define db double
#define inf 0x3f3f3f3f
using namespace std;
const int N=2e5+5;
vector<pair<int,int>>G[N];
int n,rt,num,ps,sz[N],ban[N],fa[N];
ll w[N],dis[N],res;
vector<pair<ll,pair<int,int>>>edges;
void dfs(int u,int topf){
sz[u]=1;
for(auto v:G[u]){
if(v.fi==topf||ban[v.fi])continue;
dfs(v.fi,u),sz[u]+=sz[v.fi];
}
}
void dfs2(int u,int topf){
for(auto v:G[u]){
if(v.fi==topf||ban[v.fi])continue;
dfs2(v.fi,u);
}
if(rt==-1&&sz[u]>=(num+1)/2)rt=u;
}
void locate(int u,int topf){
if(ps==-1||dis[u]+w[u]<dis[ps]+w[ps])ps=u;
for(auto v:G[u]){
if(v.fi==topf||ban[v.fi])continue;
dis[v.fi]=dis[u]+v.se,locate(v.fi,u);
}
}
void connect(int u,int topf){
edges.pb(make_pair(dis[ps]+w[ps]+dis[u]+w[u],make_pair(ps,u)));
for(auto v:G[u]){
if(v.fi==topf||ban[v.fi])continue;
connect(v.fi,u);
}
}
void solve(int u){
dfs(u,0),rt=-1,num=sz[u],dfs2(u,0);
dis[rt]=0,ps=-1,locate(rt,0);
connect(rt,0);ban[rt]=1;
for(auto v:G[rt])if(!ban[v.fi])solve(v.fi);
}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;for(int i=1;i<=n;i++)cin>>w[i];
for(int i=1;i<n;i++){
int x,y,z;cin>>x>>y>>z;
G[x].pb(make_pair(y,z));
G[y].pb(make_pair(x,z));
}
solve(1);
sort(edges.begin(),edges.end());
for(int i=1;i<=n;i++)fa[i]=i;
for(auto e:edges){
ll z=e.fi;int x=e.se.fi,y=e.se.se;
x=find(x),y=find(y);
if(x!=y)fa[x]=y,res+=z;
}
cout<<res;
}