AT3611 Tree MST
一、题目
二、解法
我自己做的时候用 \(\tt Boruvka\) 加点分治,一直 \(\tt T\) 三个点根本卡不过去。
其实可以直接点分治,因为我们只要考虑了每条路径就考虑了每条边,那么对于每个分治中心,我们求出子树内到中心点的距离 \(dis[i]\),记 \(p[i]=w[i]+dis[i]\),找到 \(p[i]\) 最小的点和每个点配对即可,不需要考虑在同一个子树内的情况,因为这种边一定不优。
那么我们通过点分治得到了 \(O(n\log n)\) 条边,跑 \(\tt kruskall\) 就可以做到 \(O(n\log^2n)\)
如果要用 \(\tt Boruvka\) 也可以,每次跑两遍 \(dp\) 处理出最优匹配点即可,时间复杂度 \(O(n\log n)\)
三、总结
最小生成树还有一种重要的思路是保留有用的边。
路径问题考虑点分治。
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 200005;
#define int long long
const int inf = 1e18;
#define pii pair<int,int>
#define mp make_pair
#define x first
#define y second
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,sz,tot,ans,f[M];pii rt,mi;
int fa[M],siz[M],w[M],dis[M],vis[M];
struct edge
{
int v,c,next;
}e[2*M];
struct node
{
int u,v,c;
bool operator < (const node &b) const
{
return c<b.c;
}
};vector<node> v;vector<int> nw;
void findrt(int u,int fa)
{
siz[u]=1;int mx=0;
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(vis[v] || v==fa) continue;
findrt(v,u);
siz[u]+=siz[v];
mx=max(mx,siz[v]);
}
mx=max(mx,sz-siz[u]);
rt=min(rt,mp(mx,u));
}
void dfs(int u,int fa)
{
mi=min(mi,mp(dis[u]+w[u],u));
nw.push_back(u);
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==fa || vis[v]) continue;
dis[v]=dis[u]+e[i].c;
dfs(v,u);
}
}
void solve(int u)
{
vis[u]=1;dis[u]=0;mi=mp(inf,0);
nw.clear();dfs(u,u);
for(auto x:nw)
v.push_back(node{x,mi.y,mi.x+dis[x]+w[x]});
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(vis[v]) continue;
rt=mp(n,0);sz=siz[v];
findrt(v,0);solve(rt.y);
}
}
int find(int x)
{
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
void kruskall()
{
sort(v.begin(),v.end());
for(int i=1;i<=n;i++) fa[i]=i;
for(auto t:v)
{
int x=find(t.u),y=find(t.v);
if(x==y) continue;
fa[x]=y;ans+=t.c;
}
}
signed main()
{
freopen("zxy.in","r",stdin);
n=read();
for(int i=1;i<=n;i++)
w[i]=read();
for(int i=1;i<n;i++)
{
int u=read(),v=read(),c=read();
e[++tot]=edge{v,c,f[u]},f[u]=tot;
e[++tot]=edge{u,c,f[v]},f[v]=tot;
}
sz=n;rt=mp(n,0);
findrt(1,0);solve(rt.y);
kruskall();
printf("%lld\n",ans);
}