给出两棵 n 结点的有标号树。
每次操作删去第一棵树的一条边,再加上一条边,需要保证此时还是一棵树。
构造一种操作序列,将第一棵树变成第二棵树,使得操作数最小。
n ≤
5
×
1
0
5
5 \times 10^5
5×105
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int N=5e5+5;
struct Edge{
int v,nxt;
}e1[N<<1],e2[N<<1];
int n,head1[N],cnt1,head2[N],cnt2,fa1[N],fa2[N];
int bel[N];
struct Ans{int a,b,c,d;};
vector<Ans> ans;
void add1(int u,int v){
e1[++cnt1].v=v;e1[cnt1].nxt=head1[u];head1[u]=cnt1;
}
void add2(int u,int v){
e2[++cnt2].v=v;e2[cnt2].nxt=head2[u];head2[u]=cnt2;
}
void dfs1(int u,int f){
for(int i=head1[u];i;i=e1[i].nxt){
int v=e1[i].v;
if(v==f) continue;
fa1[v]=u;
dfs1(v,u);
}
}
void dfs2(int u,int f){
for(int i=head2[u];i;i=e2[i].nxt){
int v=e2[i].v;
if(v==f) continue;
fa2[v]=u;
dfs2(v,u);
}
}
int find(int x){
if(bel[x]==x) return x;
return bel[x]=find(bel[x]);
}
void rebuild(int u){
for(int i=head1[u];i;i=e1[i].nxt){
int v=e1[i].v;
if(v==fa1[u]) continue;
rebuild(v);
if(u!=fa2[v]&&v!=fa2[u])
ans.push_back((Ans){u,v,find(v),fa2[find(v)]});
}
}
int main(){
scanf("%d",&n);
int x,y;
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
add1(x,y);add1(y,x);
}
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
add2(x,y);add2(y,x);
}
dfs1(1,0);dfs2(1,0);
bel[1]=1;
for(int i=2;i<=n;i++)
bel[i]=(fa1[i]==fa2[i]||fa1[fa2[i]]==i)?fa2[i]:i;
rebuild(1);
printf("%d\n",ans.size());
for(int i=0;i<ans.size();i++)
printf("%d %d %d %d\n",ans[i].a,ans[i].b,ans[i].c,ans[i].d);
return 0;
}
参考文章:
https://blog.csdn.net/u014664226/article/details/50901616