AT3611 Tree MST


题面传送门
这道题好像有两种做法。
做法一直接无脑上Boruvka,然后在树上dp,保存最近的值和和最近值颜色不同的最近值。
但是好难写,不想写怎么办。
考虑MST的一个性质:将边集分成两部分,对两部分分别做MST,然后将两边剩下的边合起来做MST就是现在的MST了。
所以可以考虑淀粉质。
可以确定重心以后算答案,然后两点之间的距离就是两个点到重心的距离和。
所以树内部的边集可以递归下去处理,树之间的边集直接找到\(W_x+D_x\)最小的点让所有点向它连边即可。
然后合并以后跑一遍MST即可。
时间复杂度\(O(n\log^2n)\)
code:

#include
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define RI re int
#define ll long long
#define db double
#define lb long db
#define N (200000+5)
#define M (1<<23)+5
#define mod 1000000007
#define Mod (mod-1)
#define eps (1e-9)
#define U unsigned int
#define it iterator
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int n,m,k,Si[N+5],vis[N+5],dp[N+5],x,y,z,cnt,Mx,fa[N+5],W[N+5];ll D[N+5],Ans,P[N+5];
I int GF(int x){return fa[x]^x?fa[x]=GF(fa[x]):x;}
struct Edge{int to,w;};vector S[N+5];struct Ques{int x,y;ll z;};vector E;I bool cmp(Ques x,Ques y){return x.z &B){B.PB((Ques){x,Mx,P[x]+P[Mx]});for(Edge i:S[x]) i.to^La&&!vis[i.to]&&(Lk(i.to,x,B),0);}
I vector Solve(int x,int La){
	cnt=0;GS(x,La);Mx=0;DP(x,La);x=Mx;vis[x]=1;vector E,Fs;E.clear();Mx=x;D[x]=0;P[x]=W[x];calc(x,0);Lk(x,0,E);for(Edge i:S[x]){if(vis[i.to])continue;Fs=Solve(i.to,x);
	for(Ques d:Fs) E.PB(d);}Fs=E;sort(Fs.begin(),Fs.end(),cmp);E.clear();for(Ques i:Fs) GF(i.x)^GF(i.y)&&(E.PB(i),fa[GF(i.x)]=GF(i.y));for(Ques i:E) fa[i.x]=i.x,fa[i.y]=i.y;return E; 
}
int main(){
	freopen("1.in","r",stdin);
	RI i;scanf("%d",&n);dp[0]=1e9;for(i=1;i<=n;i++) scanf("%d",&W[i]),fa[i]=i;for(i=1;i<=n;i++) scanf("%d%d%d",&x,&y,&z),S[x].PB((Edge){y,z}),S[y].PB((Edge){x,z});
	E=Solve(1,0);for(Ques d:E) Ans+=d.z;printf("%lld\n",Ans);
}