河童重工,恐怖如斯


博客园 ver.

显然是 Boruvka,考虑一次怎么求边。

一棵树的时候是简单的,树形 dp 即可。考虑第二棵树是菊花怎么做,显然这个问题等价于完全图上的边权形如 \(D(x,y)+a_x+a_y\),这也是简单的,推广这个做法,对第二棵树点分治,每次对不同子树中的点统计贡献,这样每个点就有了两个属性(原图上的颜色、第二棵树上所在的子树),不妨分别记为 \(col_i\)\(bel_i\),那我们就是要找两个 \(bel_i\neq bel_j\)\(col_i\neq col_j\) 的点。多记一下状态大力分类讨论转移可以解决。考虑是否真要这么麻烦呢?如果我们不区分 \(bel_i\),考虑两个点 \((i,j)\),他们可能会在点分治时产生贡献,但一定不是最优,考虑两者在不同子树一定比当前优,因此无需考虑区分 \(bel_i\)

还有一个问题就是如果每次点分治时都对原树做一遍 dp,复杂度为 \(O(n^2)\)。考虑点分治时只会有 \(O(n\log n)\) 个点有用,因此只需对其建虚树即可,可以做到 \(O(n\log^3 n)\),由于每次做 Boruvka 的时候虚树形态相同,因此可以提前预处理出来。时间复杂度 \(O(n\log^2 n)\)

代码里面有份暴力,所以看上去比较长。


洛谷 ver.

完全图最小生成树,考虑 Boruvka。

先来考虑一个弱化弱化版,只有一棵树,边权为 \(a_x+a_y+dis(x,y)\) 应该怎么做,每轮做树形 dp,维护子树内最大值和颜色不同的次大值,时间复杂度 \(O(n\log n)\)

再来考虑一个弱化版,第二棵树是菊花应该怎么做,显然这个问题和上面那个等价,菊花图上任意两个点之间的距离就是两个点到根的距离之和,时间复杂度 \(O(n\log n)\)

回到这个问题,我们想转化到弱化版的问题,即距离可以写成两个数的和的形式,不难想到点分治,对第二棵树点分治,这样每对点之间的距离就能写成 \(a_x+a_y\) 的形式,然后做弱化弱化版的树形 dp 即可。注意此处的限制是要求两个点在之前的 Boruvka 中不在同一个连通块内,同时点分治不在不同一个子树中,看上去 dp 的时候要记的状态比较多,不过没有必要记是否在同一个子树中,这样显然不会最优。

这样的复杂度还是不对,我们每次点分治都要对第一棵树做 \(O(n)\) 的树形 dp,很糟糕,点分治这一算法能优化复杂度的主要地方在于点的总数是 \(O(n\log n)\) 的,启发我们建出虚树,然后在虚树上 dp。时间复杂度 \(O(n\log^3 n)\),三个 \(\log\) 分别来自 Boruvka、点分治、建虚树。

可以发现每轮 Boruvka 虚树形态是固定的,因此可以提前建好虚树,时间复杂度为 \(O(n\log^2 n)\)


#include<bits/stdc++.h>
namespace brute_force{
// #include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define pii std::pair<int,int>
#define vint std::vector<int>
#define vpair std::vector<pii>
#define debug(...) fprintf(stderr,##__VA_ARGS__)

template<typename T>
void read(T &x){
	x=0;
	int f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
	x*=f;
}

std::stack<char>st;
template<typename T>
void print(T x){
	if(x==0) putchar('0');
	if(x<0) putchar('-'),x=-x;
	while(st.size()) st.pop();
	while(x) st.push((char)('0'+x%10)),x/=10;
	while(st.size()) putchar(st.top()),st.pop();
}

template<typename T>
void printsp(T x){
	print(x),putchar(' ');
}

template<typename T>
void println(T x){
	print(x),putchar('\n');
}

template<typename T,typename I>
bool chkmin(T &a,I b){
	if(a>b) return a=b,1;
	return 0;
}

template<typename T,typename I>
bool chkmax(T &a,I b){
	if(a<b) return a=b,1;
	return 0;
}

template<typename T,typename I>
void addedge(std::vector<I>*vec,T u,T v){
	vec[u].push_back(v);
}

template<typename T,typename I,typename K>
void addedge(std::vector<K>*vec,T u,T v,I w){
	vec[u].push_back({v,w});
}

template<typename T,typename I>
void addd(std::vector<I>*vec,T u,T v){
	addedge(vec,u,v),addedge(vec,v,u);
}

template<typename T,typename I,typename K>
void addd(std::vector<K>*vec,T u,T v,I w){
	addedge(vec,u,v,w),addedge(vec,v,u,w);
}

bool Mbe;

const int inf=1e18,MOD1=998244353,MOD2=1e9+7;

const int maxn=1e3+10;

vpair a[maxn],b[maxn],c[maxn];

int n,dis1[maxn],dis2[maxn],f[maxn][20],g[maxn][20],dep1[maxn],dep2[maxn];

struct edge{
	int u,v,w;
}d[maxn*maxn];

namespace DSU{
	int to[maxn];
	void init_(){
		for(int i=1;i<=n;i++) to[i]=i;
	}
	int go(int x){
		if(to[x]==x) return x;
		return to[x]=go(to[x]);
	}
	bool merge(int x,int y){
		x=go(x),y=go(y);
		if(x==y) return 0;
		to[x]=y;
		return 1;
	}
}

using namespace DSU;

void dfs1(int p,int fa){
	dep1[p]=dep1[fa]+1;
	f[p][0]=fa;
	for(pii i:a[p]){
		if(i.fi==fa) continue;
		dis1[i.fi]=dis1[p]+i.se;
		dfs1(i.fi,p);
	}
}

void dfs2(int p,int fa){
	dep2[p]=dep2[fa]+1;
	g[p][0]=fa;
	for(pii i:b[p]){
		if(i.fi==fa) continue;
		dis2[i.fi]=dis2[p]+i.se;
		dfs2(i.fi,p);
	}
}

edge made(int u,int v,int w){
	edge res;
	res.u=u,res.v=v,res.w=w;
	return res;
}

void init(){
	for(int j=1;j<=19;j++)
		for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1],g[i][j]=g[g[i][j-1]][j-1];
}

int lca1(int x,int y){
	if(dep1[x]<dep1[y]) std::swap(x,y);
	for(int i=19;i>=0;i--)
		if(dep1[f[x][i]]>=dep1[y]) x=f[x][i];
	if(x==y) return x;
	for(int i=19;i>=0;i--)
		if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}

int lca2(int x,int y){
	if(dep2[x]<dep2[y]) std::swap(x,y);
	for(int i=19;i>=0;i--)
		if(dep2[g[x][i]]>=dep2[y]) x=g[x][i];
	if(x==y) return x;
	for(int i=19;i>=0;i--)
		if(g[x][i]!=g[y][i]) x=g[x][i],y=g[y][i];
	return g[x][0];
}

bool cmp(edge s1,edge s2){
	return s1.w<s2.w;
}

bool Men;

void main(){
	debug("%.6lfMB\n",(&Mbe-&Men)/1048576.0);
	read(n);
	int x,y,v;
	#define sb(a) read(x),read(y),read(v),addd(a,x,y,v)
	for(int i=1;i<n;i++) sb(a);
	for(int i=1;i<n;i++) sb(b);
	dfs1(1,0),dfs2(1,0),init();
	int cnt=0;
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++){
			int cost=dis1[i]+dis1[j]-2*dis1[lca1(i,j)]+dis2[i]+dis2[j]-2*dis2[lca2(i,j)];
			// addd(a,i,j,cost);
			d[++cnt].u=i,d[cnt].v=j,d[cnt].w=cost;
		}
	init_();
	std::sort(d+1,d+cnt+1,cmp);
	int cost=0;
	std::vector<edge>vec;
	for(int i=1;i<=cnt;i++){
		int u=d[i].u,v=d[i].v,w=d[i].w;
		if(!merge(u,v)) continue;
		cost+=w;
		vec.push_back(made(u,v,w));
		// addd(c,u,v);
	}
	println(cost);
	for(edge i:vec) printf("%lld %lld %lld\n",i.u,i.v,i.w);
	// for(int i=1;i<=n;i++)
		// for(int j:vost)
	debug("%.6lfms\n",1e3*clock()/CLOCKS_PER_SEC);
}}
#define int long long
#define fi first
#define se second
#define pii std::pair<int,int>
#define vint std::vector<int>
#define vpair std::vector<pii>
#define debug(...) fprintf(stderr,##__VA_ARGS__)

template<typename T>
void read(T &x){
	x=0;
	int f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
	x*=f;
}

std::stack<char>st;
template<typename T>
void print(T x){
	if(x==0) putchar('0');
	if(x<0) putchar('-'),x=-x;
	while(st.size()) st.pop();
	while(x) st.push((char)('0'+x%10)),x/=10;
	while(st.size()) putchar(st.top()),st.pop();
}

template<typename T>
void printsp(T x){
	print(x),putchar(' ');
}

template<typename T>
void println(T x){
	print(x),putchar('\n');
}

template<typename T,typename I>
bool chkmin(T &a,I b){
	if(a>b) return a=b,1;
	return 0;
}

template<typename T,typename I>
bool chkmax(T &a,I b){
	if(a<b) return a=b,1;
	return 0;
}

template<typename T,typename I>
void addedge(std::vector<I>*vec,T u,T v){
	vec[u].push_back(v);
}

template<typename T,typename I,typename K>
void addedge(std::vector<K>*vec,T u,T v,I w){
	vec[u].push_back({v,w});
}

template<typename T,typename I>
void addd(std::vector<I>*vec,T u,T v){
	addedge(vec,u,v),addedge(vec,v,u);
}

template<typename T,typename I,typename K>
void addd(std::vector<K>*vec,T u,T v,I w){
	addedge(vec,u,v,w),addedge(vec,v,u,w);
}

bool Mbe;

const int inf=1e18,MOD1=998244353,MOD2=1e9+7;

const int maxn=1e5+10;

int n,num,sz[maxn],mx[maxn],dfn[maxn],edfn[maxn*2],cnt,tot,fir[maxn],fdfn[maxn],mt[maxn*2][18],lg[maxn*2],dis[maxn],bel[maxn],val[maxn],best[maxn],sec[maxn],bcol[maxn],seccol[maxn];

bool vis[maxn];

vint col[maxn],vec;

vpair a[maxn],b[maxn],mst;

struct edge{
	int u,v,w;
};

edge newedge(int u,int v,int w){
	edge res;
	res.u=u,res.v=v,res.w=w;
	return res;
}

struct Tree{
	int rt;
	std::vector<edge>a;
}dot[maxn];

namespace LCA{
	void dfs(int p,int fa){
		dfn[p]=++cnt,edfn[++tot]=dfn[p];
		fir[p]=tot,fdfn[cnt]=p;
		for(pii i:a[p]){
			if(i.fi==fa) continue;
			dis[i.fi]=dis[p]+i.se;
			dfs(i.fi,p);
			edfn[++tot]=dfn[p];
		}
	}
	void init(){
		dfs(1,0);
		for(int i=1;i<=tot;i++) mt[i][0]=edfn[i];
		for(int j=1;j<20;j++)
			for(int i=1;i+(1ll<<j)-1<=tot;i++) mt[i][j]=std::min(mt[i][j-1],mt[i+(1ll<<(j-1))][j-1]);
		lg[0]=-1;
		for(int i=1;i<=tot;i++) lg[i]=lg[i>>1]+1;
	}
	int query(int l,int r){
		if(l>r) std::swap(l,r);
		int len=r-l+1;
		return std::min(mt[l][lg[len]],mt[r-(1ll<<lg[len])+1][lg[len]]);
	}
	int lca(int x,int y){
		return fdfn[query(fir[x],fir[y])];
	}
}

void pdfs(int p,int fa){
	vec.push_back(p);
	sz[p]=1;
	mx[p]=0;
	for(pii i:b[p]){
		if(i.fi==fa||vis[i.fi]) continue;
		pdfs(i.fi,p);
		sz[p]+=sz[i.fi];
		chkmax(mx[p],sz[i.fi]);
	}
	// debug("p=%lld sz=%lld mx=%lld\n",p,sz[p],mx[p]);
}

int zx,siz;

void findrt(int p,int fa,int gt){
	// debug("p=%lld gt=%lld mx=%llld siz=%lld\n",p,gt,mx[p],siz);;
	if(std::max(gt,mx[p])<siz) zx=p,siz=std::max(gt,mx[p]);
	for(pii i:b[p]){
		if(i.fi==fa||vis[i.fi]) continue;
		findrt(i.fi,p,gt+sz[p]-sz[i.fi]);
	}
}

bool cmp_dfn(int x,int y){
	return dfn[x]<dfn[y];
}

void dfs(int p){
	vec.clear();
	siz=inf;
	pdfs(p,0),findrt(p,0,0);
	vint nd;
	// debug("zx=%lld\n",zx);
	std::sort(vec.begin(),vec.end(),cmp_dfn);
	// for(int i:vec) debug("i=%lld\n",i);
	for(int i=1;i<vec.size();i++){
		int lca=LCA::lca(vec[i],vec[i-1]);
		nd.push_back(lca);
		// debug("%lld %lld lca:%lld\n",vec[i],vec[i-1],lca);
		// dot[zx].a.push_back(newedge(vec[i],lca,dis[vec[i]]-dis[lca]));
		// debug("%lld - %lld > %lld\n",vec[i],lca,dis[vec[i]]-dis[lca]);	
		// debug("zx=%lld %lld %lld\n",zx,vec[i])
	}
	for(int i:nd) vec.push_back(i);
	std::sort(vec.begin(),vec.end(),cmp_dfn);
	auto z=std::unique(vec.begin(),vec.end());
	auto i=vec.begin();
	int las=*i;
	i++;
	for(;i!=z;i++){
		int now=*i;
		int lca=LCA::lca(las,now);
		dot[zx].a.push_back(newedge(now,lca,dis[now]-dis[lca]));
		// debug("%lld - %lld > %lld\n",now,lca,dis[now]-dis[lca]);
		las=now;
	}
	int mi=inf;
	for(int i:vec) if(chkmin(mi,dfn[i])) dot[zx].rt=i;
	vis[zx]=1;
	for(pii i:b[zx]) if(!vis[i.fi]) dfs(i.fi);
}

int cost;

namespace DSU{
	int to[maxn];
	void init(){
		for(int i=1;i<=n;i++) to[i]=i;
	}
	int go(int p){
		if(to[p]==p) return p;
		return to[p]=go(to[p]);
	}
	void merge(int x,int y){
		x=go(x),y=go(y);
		if(x==y) return ;
		to[x]=y;
	}
}

int ok[maxn],tim;

namespace getMST{
	int sz[maxn],mx[maxn],zx,siz,cho[maxn],id[maxn];
	bool vis[maxn];
	vpair c[maxn];
	namespace VT{
		void dfs(int p,int fa){
			for(pii i:c[p]){
				if(i.fi==fa) continue;
				dfs(i.fi,p);
			}
			if(ok[p]!=tim) val[p]=inf;
			best[p]=val[p],bcol[p]=bel[p];
			sec[p]=inf,seccol[p]=0;
			for(pii i:c[p]){
				if(i.fi==fa) continue;
				if(best[i.fi]+i.se<=best[p]){
					if(bcol[i.fi]==bcol[p]) best[p]=best[i.fi]+i.se;
					else sec[p]=best[p],seccol[p]=bcol[p],best[p]=best[i.fi]+i.se,bcol[p]=bcol[i.fi];
				}else if(best[i.fi]+i.se<=sec[p]&&bcol[p]!=bcol[i.fi]){
					sec[p]=best[i.fi]+i.se,seccol[p]=bcol[i.fi];
				}
				if(sec[i.fi]+i.se<=sec[p]&&seccol[i.fi]!=bcol[p]) sec[p]=sec[i.fi]+i.se,seccol[p]=seccol[i.fi];
			}
			// best[p]+=val[p],sec[p]+=val[p];
			// debug("p=%lld best=%lld bcol=%lld sec=%lld seccol=%lld\n",p,best[p],bcol[p],sec[p],seccol[p]);
		}
		void pdfs(int p,int fa,int bst,int snd,int bstcol,int sndcol){
			int w=inf;
			if(bel[p]!=bcol[p]) chkmin(w,best[p]);
			if(bstcol!=bel[p]) chkmin(w,bst);
			if(bel[p]!=seccol[p]) chkmin(w,sec[p]);
			if(bel[p]!=sndcol) chkmin(w,snd);
			if(chkmin(cho[bel[p]],w+val[p])){
				if(w==best[p]&&bcol[p]!=bel[p]) id[bel[p]]=bcol[p];
				else if(w==bst&&bstcol!=bel[p]) id[bel[p]]=bstcol;
				else if(w==snd&&sndcol!=bel[p]) id[bel[p]]=sndcol;
				else id[bel[p]]=seccol[p];
			}
			// debug("p=%lld bst=%lld snd=%lld bstcol=%lld sndcol=%lld w=%lld cho=%lld id=%lld\n",p,bst,snd,bstcol,sndcol,w,cho[bel[p]],id[bel[p]]);
			int bw=std::min(std::min(std::min(bst,snd),best[p]),sec[p]),bc;
			int cw=inf,cc;
			if(bw==best[p]) bc=bcol[p];
			else bc=bstcol;
			if(snd<cw&&sndcol!=bc) cw=snd,cc=sndcol;
			if(sec[p]<cw&&seccol[p]!=bc) cw=sec[p],cc=seccol[p];
			// if(cw==snd) cc=sndcol;
			// else cc=seccol[p];
			if(bw==best[p]&&bcol[p]!=bstcol&&bst<cw) cw=bst,cc=bstcol;
			if(bw==bst&&bstcol!=bcol[p]&&best[p]<cw) cw=best[p],cc=bcol[p];
			for(pii i:c[p]){
				if(i.fi==fa) continue;
				pdfs(i.fi,p,bw+i.se,cw+i.se,bc,cc);
			}
		}
	}
	void pdfs(int p,int fa){
		sz[p]=1,mx[p]=0,ok[p]=tim;
		for(pii i:b[p]){
			if(i.fi==fa||vis[i.fi]) continue;
			pdfs(i.fi,p);
			sz[p]+=sz[i.fi];
			chkmax(mx[p],sz[i.fi]);
		}
	}
	void findrt(int p,int fa,int gt){
		if(chkmin(siz,std::max(mx[p],gt))) zx=p;
		for(pii i:b[p]){
			if(i.fi==fa||vis[i.fi]) continue;;
			findrt(i.fi,p,gt+sz[p]-sz[i.fi]);
		}
	}
	void getval(int p,int fa){
		for(pii i:b[p]){
			if(i.fi==fa||vis[i.fi]) continue;
			val[i.fi]=val[p]+i.se;
			// debug("i.fi=%lld val=%lld p=%lld i.se=%lld\n",i.fi,val[i.fi],p,i.se);
			getval(i.fi,p);
		}
	}
	void dfs(int p){
		tim++;
		siz=inf;
		pdfs(p,0),findrt(p,0,0);
		p=zx;
		// debug("zx=%lld\n",zx);
		val[p]=0,getval(p,0);
		for(edge i:dot[p].a){
			addd(c,i.u,i.v,i.w);
		}
		VT::dfs(dot[p].rt,0),VT::pdfs(dot[p].rt,0,inf,inf,-1,-1);
		for(edge i:dot[p].a) c[i.u].clear(),c[i.v].clear();
		vis[zx]=1;
		for(pii i:b[p]){
			if(vis[i.fi]) continue;
			dfs(i.fi);
		}
	}
	int to[maxn];
	void Boruvka(){
		// debug("ok\n");
		// DSU::init();
		for(int i=1;i<=n;i++)
			for(int j:col[i]) bel[j]=i,cho[i]=inf,id[i]=i;//,debug("i=%lld j=%lld\n",i,j);
		memset(vis,0,sizeof(vis));
		dfs(1);
		for(int i=1;i<=n;i++){
			if(!col[i].size()) continue;
			if(DSU::go(i)==DSU::go(id[i])) continue;
			// if(id[i]<i) continue;
			cost+=cho[i];
			num--;
			DSU::merge(i,id[i]);
			// for(int j:col[id[i]]) col[i].push_back(j);
		}
		for(int i=1;i<=n;i++) col[i].clear();
		for(int i=1;i<=n;i++) col[DSU::go(i)].push_back(i);
	}
}

bool Men;

signed main(){
	// freopen("data.in","r",stdin),freopen("my.out","w",stdout);
	// brute_force::main();
	// return 0;
	debug("%.6lfMB\n",(&Mbe-&Men)/1048576.0);
	read(n);
	for(int i=1;i<n;i++){
		int x,y,v;
		read(x),read(y),read(v);
		addd(a,x,y,v);
	}
	LCA::init();
	for(int i=1;i<n;i++){
		int x,y,v;
		read(x),read(y),read(v);
		addd(b,x,y,v);
	}
	dfs(1);
	for(int i=1;i<=n;i++) col[i].push_back(i);
	num=n;
	DSU::init();
	while(num!=1) getMST::Boruvka();
	println(cost);
	debug("%.6lfms\n",1e3*clock()/CLOCKS_PER_SEC);
}
/*
5
2 1 12
3 1 3
4 2 6
5 4 1
2 1 9
3 2 14
4 3 4
5 3 13

5
2 1 14
3 2 6
4 1 13
5 4 14
2 1 8
3 2 4
4 3 15
5 1 7


*/
posted @ 2025-02-08 19:21  BYR_KKK  阅读(43)  评论(0)    收藏  举报