Description:
一棵樹的邊全部是藍色的,每次可以選擇一條全是藍色邊的鏈,刪除其中一條邊並用紅邊連接鏈的端點。問是否可以變成另一棵樹。
Solution:
倒着考慮這個過程,發現每次相當於連接兩個同時在兩棵樹都有邊的兩個點,然後縮成一個連通塊。那麼用並查集維護連通塊, 維護相鄰的點,啓發式合併就行了。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int n;
int fa[maxn];
set<int> G[maxn], g[maxn];
queue<pair<int, int> > q;
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
pair<int, int> mp(int x, int y) {
if(x > y) {
swap(x, y);
}
return make_pair(x, y);
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
fa[i] = i;
}
for(int i = 1; i < n; ++i) {
int u, v;
scanf("%d%d", &u, &v);
G[u].insert(v);
G[v].insert(u);
}
for(int i = 1; i < n; ++i) {
int u, v;
scanf("%d%d", &u, &v);
g[u].insert(v);
g[v].insert(u);
if(G[u].find(v) != G[u].end()) {
q.push(mp(u, v));
}
}
for(int i = 1, u, v; i < n; ++i) {
do {
if(q.empty()) {
return puts("NO"), 0;
}
u = find(q.front().first);
v = find(q.front().second);
q.pop();
} while(u == v);
if(g[u].size() > g[v].size()) {
swap(u, v);
}
g[u].erase(v);
g[v].erase(u);
G[u].erase(v);
G[v].erase(u);
fa[u] = v;
set<int> :: iterator it;
for(it = G[u].begin(); it != G[u].end(); ++it) {
if(g[*it].find(v) != g[*it].end()) {
q.push(mp(*it, v));
}
}
for(it = g[u].begin(); it != g[u].end(); ++it) {
if(G[*it].find(v) != G[*it].end()) {
q.push(mp(*it, v));
}
}
for(it = G[u].begin(); it != G[u].end(); ++it) {
G[*it].erase(u);
G[*it].insert(v);
G[v].insert(*it);
}
for(it = g[u].begin(); it != g[u].end(); ++it) {
g[*it].erase(u);
g[*it].insert(v);
g[v].insert(*it);
}
}
return puts("YES"), 0;
}