【题解】CF650E-Clockwork Bomb
给定两棵树 \(A,B\),每次先删除 \(A\) 一条边,再加入 \(A\) 一条边,过程中不能成环,现在构造一个方案用最少次操作将 \(A\) 变成 \(B\)。
显然两棵树中都存在的边可以不用删,我们将这些边留下,将每个连通块缩成一个点。
那么我们得到两颗大小相同的新树,且不存在一条边在两棵树内都出现。
考虑从叶子开始删除,每次删除一个叶子与其父节点的边,然后连上需要还原的 \(B\) 边。操作完成后叶子的父边算是还原了,就可以直接将叶子删除。由于每次删除都是分裂出一个节点,再把这个节点接回去,所以是正确的。
时间复杂度 \(\mathcal{O}(N)\)。
#define N 500005
struct Tree{
vector<int>e[N]; int fa[N];
void dfs(int x,int f){fa[x] = f; go(y, e[x])if(y != f)dfs(y, x);}
}A, B;
int n, fa[N];
int get(int x){return fa[x] == x ? x : fa[x] = get(fa[x]);}
struct node{int x, y, a, b;};
vector<node>ed;
void dfs(int x,int f){
go(y, A.e[x])if(y != f){
dfs(y, x);
if(fa[y] != fa[x])ed.pb(node{x, y, fa[y], B.fa[fa[y]]});
}
}
int main() {
read(n);
rp(i, n - 1){
int x, y; read(x, y);
A.e[x].pb(y), A.e[y].pb(x);
}
rp(i, n - 1){
int x, y; read(x, y);
B.e[x].pb(y), B.e[y].pb(x);
}
A.dfs(1, 0), B.dfs(1, 0);
rp(i, n)fa[i] = i;
rep(x, 2, n){
int y = B.fa[x];
if(A.fa[x] == y || A.fa[y] == x)fa[x] = y;
}
rp(i, n)get(i);
dfs(1, 0);
printf("%d\n", si(ed));
go(x, ed)printf("%d %d %d %d\n", x.x, x.y, x.a, x.b);
return 0;
}