河童重工 题解
题意
给定两棵 \(n\) 个点的带边权树。
求包含 \(n\) 个点的,\(i\) 和 \(j\) 之间边权为 \(d_1(i, j) + d_2(i, j)\) 的完全图的最小生成树大小。
其中 \(d_x(i, j)\) 表示 \(i\) 和 \(j\) 两点在第 \(x\) 棵树上的距离
\(1 ≤ n ≤ 10^5, 0 ≤ w ≤ 500\)
题解
完全图求 \(\text{MST}\) ,首先想到 \(\text{Boruka}\) ,但边权跟两棵树有关,不会了?
对一个图求 \(\text{MST}\) 有一个性质:把 \(G\) 划分为几个子图分别求 \(\text{MST}\) ,把留下的边合起来再求 \(\text{MST}\),与原图 \(\text{MST}\) 相同。
证明即根据 \(Kruskal\) 算法,划分为子图后 不会被丢掉的边一定不会被丢掉 。
那么怎么划分边集使其好计算呢?
诶,完全图的边集就是树上的所有路径构成的集合对吧。所有路径,点分治!
对 \(Tree_2\) 点分治,考虑计算当前分治块内、跨过分治中心的路径在完全图上的边构成的 \(\text{MST}\)
把与分治中心的距离当做点权,因为同一子树内的路径在之后会计算,此时加进去不会对答案有影响,所以只用考虑 \(Tree_1\) 的贡献。
对于 \(Tree_1\) ,问题变为点有点权 \(w\) ,求完全图的 \(\text{MST}\) ,\(e(u,v)=w_u+w_v+dis(u,v)\) 。如果你做过 \(Tree MST\) 那么此时已经做完了。
我们运用 \(\text{Boruka}\) ,每轮可以用 \(DP\) + 换根 \(DP\) 记录每个点最近的点和最近且颜色不同的点 和 其当前联通块颜色,然后把每个点贡献给其联通块即可。
为了保证复杂度,需要用到虚树,本来不存在的点的 \(w\) 赋为 \(\infty\) 即可,需要注意的是对虚树跑 \(\text{Boruka}\) 要在建虚树后更新树大小。
Code
//from 2022.3.19 18:44
#include<bits/stdc++.h>
#define ri register int
#define ll long long
#define pii pair<int,ll>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int maxn = 1e5 + 10;
const ll inf = 0x3f3f3f3f3f3f3f3f,mx = 1e10;
template<class T> inline void rd(T &x){
x = 0; char ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = x * 10 + ch - 48,ch = getchar();
}
int cnt,tmp[maxn],dsu[maxn];
ll w[maxn];
inline int find(int x) {return (dsu[x] == x) ? dsu[x] : (dsu[x] = find(dsu[x]));}
inline void merge(int u,int v) {u = find(u),v = find(v),dsu[u] = v;}
int dfn[maxn],n;
inline bool cmp(const int &x,const int &y) {return dfn[x] < dfn[y];}
struct edge{
int u,v; ll w;
friend bool operator <(edge x,edge y){return x.w < y.w;}
};
vector<edge> e;
struct Tree1{
struct node{pii mn,sec;}f[maxn];
int top[maxn],fa[maxn],sz[maxn],son[maxn],dep[maxn],c[maxn],dfnum,fr[maxn],st[maxn],stop;
pii best[maxn];
ll dis[maxn];
vector<pii > to[maxn];
vector<int> cur;
void dfs1(int u,int pa){
sz[u] = 1;
for(auto v : to[u])
if(v.fi ^ pa){
dep[v.fi] = dep[u] + 1,fa[v.fi] = u,dis[v.fi] = dis[u] + v.se,dfs1(v.fi,u);
sz[u] += sz[v.fi]; if(sz[v.fi] > sz[son[u]]) son[u] = v.fi;
}
}
void dfs2(int u,int toop){
top[u] = toop,dfn[u] = ++dfnum;
if(son[u]) dfs2(son[u],toop);
for(auto v : to[u])
if((v.fi ^ fa[u]) && (v.fi ^ son[u])) dfs2(v.fi,v.fi);
}
inline int lca(int u,int v){
while(top[u] ^ top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u,v);
u = fa[top[u]];
}
if(dep[u] > dep[v]) swap(u,v); return u;
}
inline ll Dis(int u,int v) {return dis[u] + dis[v] - min(dis[u],dis[v])*2;}
inline void add(int u,int v,int wi) {to[u].pb(mp(v,wi)),to[v].pb(mp(u,wi));}
inline void vt(){//to bulid virtual trees
cur.resize(0); tmp[++cnt] = 1;
sort(tmp + 1,tmp + cnt + 1,cmp);
if(tmp[2] != 1) w[1] = mx;
else unique(tmp + 1,tmp + cnt + 1),cnt--;
for(ri i = 1;i <= cnt;++i) cur.pb(tmp[i]);
st[stop = 1] = 1;
for(ri i = 2;i <= cnt;++i){
int l = lca(st[stop],tmp[i]);
if(dfn[l] == dfn[st[stop]]) {st[++stop] = tmp[i]; continue;}
while(dfn[l] < dfn[st[stop-1]])
add(st[stop-1],st[stop],Dis(st[stop-1],st[stop])),stop--;
if(l == st[stop - 1]) add(st[stop],st[stop-1],Dis(st[stop],st[stop-1])),stop--;
else add(l,st[stop],Dis(l,st[stop])),st[stop] = l,w[l] = mx,cur.pb(l);
st[++stop] = tmp[i];
}
while(stop > 1) add(st[stop],st[stop-1],Dis(st[stop],st[stop-1])),stop--;
for(auto it : cur) dsu[it] = it;
}
inline void update(node &now,node tr){
if(tr.mn.se < now.mn.se){
if((c[now.mn.fi] != c[tr.mn.fi]) && (now.mn.se < tr.sec.se)) tr.sec = now.mn;
now.mn = tr.mn;
if(c[now.mn.fi] == c[now.sec.fi]) now.sec.se = inf;
if((tr.sec.se < now.sec.se) && (c[tr.sec.fi] ^ c[now.mn.fi])) now.sec = tr.sec;
}
else{
if((tr.mn.se < now.sec.se) && (c[tr.mn.fi] ^ c[now.mn.fi])) now.sec = tr.mn;
if((tr.sec.se < now.sec.se) && (c[tr.sec.fi] ^ c[now.mn.fi])) now.sec = tr.sec;
}
}
void dfs(int u,int pa){
for(auto v : to[u]) if(v.fi ^ pa) dfs(v.fi,u);
f[u].mn.se = w[u] * 2,f[u].mn.fi = u;
for(auto v : to[u])
if(v.fi ^ pa){
node tr = f[v.fi];
tr.mn.se += v.se - w[v.fi] + w[u],tr.sec.se += v.se - w[v.fi] + w[u];
update(f[u],tr);
}
}
void rdfs(int u,int pa){
for(auto v : to[u])
if(v.fi ^ pa){
int vv = v.fi,ww = v.se;
node tr = f[u];
tr.mn.se += ww - w[u] + w[vv],tr.sec.se += ww - w[u] + w[vv];
update(f[vv],tr);
rdfs(vv,u);
}
}
void boruka(){
vt(); int num = 0;
cnt = cur.size();
while(num < (cnt - 1)){
for(auto it : cur)
f[it].sec.se = inf,best[it] = mp(0,inf),c[it] = find(it);
dfs(1,1),rdfs(1,1);
for(auto it : cur){
if(f[it].mn.se == inf) continue;
pii tr = f[it].mn; if(c[it] == c[tr.fi]) tr = f[it].sec;
if(tr.se < best[c[it]].se) best[c[it]] = tr,fr[c[it]] = it;
}
for(auto it : cur)
if((find(it) == it) && (find(best[it].fi) != find(it))){
if(best[it].se == inf) continue;
e.pb((edge){fr[it],best[it].fi,best[it].se}),num++;
merge(it,best[it].fi);
}
}
for(auto it : cur) to[it].resize(0); cur.resize(0);
}
}T1;//for vir_tree,boruka
struct Tree2{
int sz[maxn],siz,rt,maxs[maxn];
bool vis[maxn]; ll dis[maxn];
vector<pii > to[maxn];
inline void add(int u,int v,int w) {to[u].pb(mp(v,w)),to[v].pb(mp(u,w));}
void dfs(int u,int f){
tmp[++cnt] = u;
for(auto v : to[u]) if(!vis[v.fi] && (v.fi ^ f)) w[v.fi] = w[u] + v.se,dfs(v.fi,u);
}
void getsz(int u,int f){
sz[u] = 1;
for(auto v : to[u]) if(!vis[v.fi] && (v.fi ^ f)) getsz(v.fi,u),sz[u] += sz[v.fi];
}
void findrt(int u,int f){
maxs[u] = 0;
for(auto v : to[u])
if(!vis[v.fi] && (v.fi ^ f)) findrt(v.fi,u),maxs[u] = max(maxs[u],sz[v.fi]);
maxs[u] = max(maxs[u],siz-sz[u]);
if(maxs[u] < maxs[rt]) rt = u;
}
void dfz(int u){
vis[u] = 1,cnt = 0,w[u] = 0,getsz(u,u),dfs(u,u);
T1.boruka();
for(auto v : to[u])
if(!vis[v.fi]) rt = 0,siz = sz[v.fi],findrt(v.fi,v.fi),dfz(rt);
}
inline void work(){
maxs[0] = n + 1,siz = n,rt = 0,getsz(1,1),findrt(1,1); dfz(rt);
};
}T2;//for dfz
int main(){
rd(n);
for(ri i = 1,u,v,w;i < n;++i) rd(u),rd(v),rd(w),T1.add(u,v,w);
for(ri i = 1,u,v,w;i < n;++i) rd(u),rd(v),rd(w),T2.add(u,v,w);
T1.dfs1(1,1),T1.dfs2(1,1); for(ri i = 1;i <= n;++i) T1.to[i].resize(0);
T2.work(); ll ans = 0;
for(ri i = 1;i <= n;++i) dsu[i] = i;
sort(e.begin(),e.end());
for(auto ei : e) if(find(ei.u) ^ find(ei.v)) merge(ei.u,ei.v),ans += ei.w;
printf("%lld\n",ans);
return 0;
}