https://ac.nowcoder.com/acm/contest/9981/C
题意
有一棵树,每个节点上都有一种颜色,要求每个红色点边上只有一个红色点,每个蓝色点边上只有一个蓝色点,问能否构造,否则输出-1。
分析
稍微画一下图可以注意到红色和蓝色都是成对出现的,也就是说用相邻的两个点对覆盖整棵树,而且不能重叠。看到点对问题可以想到二分图的染色问题,而这时我们要多处理一步,就是找出点对。
在此之前我们通过二分图判定,如果黑色点和白色点个数不相同,那么无法构成n/2个点对。排除掉不合法的形状,剩下的就是可以构造的形状了,所以放心去搞就行了。
我们发现每个子节点都只有一个父节点,因此我们可以把所有的点对找出来,每一个点对都先赋值为儿子的节点号。这里有一个问题,怎么去找点对,我们在第一次dfs的时候记录每个点的父亲节点,然后回溯的时候找点就可以了。
找到点对后就可以将两个点当成一个点,然后普通的二分图染色去搞就行了。
用了两次二分图的染色,三次dfs感觉有些麻烦了,不过思路还是比较清楚地。
Code
#include <bits/stdc++.h>
using namespace std;
//#define ACM_LOCAL
const int N = 2e5 + 10;
const int M = 5e5 + 10;
const int INF = 0x3f3f3f3f;
const double eps = 1e-4;
const int MOD = 1e9+7;
typedef long long ll;
typedef pair<int, int> PII;
vector<int> g[N];
int in[N], col[N], cnt, vis[N], fat[N], id[N];
void dfs1(int x, int fa, int color) {
col[x] = color;
if (color == 1) cnt++;
for (auto v : g[x]) {
if (v == fa) continue;
fat[v] = x;
dfs1(v, x, color ^ 1);
}
}
void dfs2(int x, int fa, int rt) {
for (auto v : g[x]) {
if (v == fa) continue;
dfs2(v, x, rt);
}
if (!vis[x]) {
vis[x] = vis[fat[x]] = 1;
col[x] = col[fat[x]] = x;
}
}
void dfs3(int x, int fa, int color) {
id[x] = color;
for (auto v : g[x]) {
if (v == fa) continue;
if (col[v] == col[x]) {
dfs3(v, x, color);
} else {
dfs3(v, x, color^1);
}
}
}
void solve() {
int n; cin >> n;
for (int i = 1; i <= n - 1; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
if (n % 2 != 0) {
cout << -1 << endl;
return;
}
memset(col, -1, sizeof col);
dfs1(1, 0, 0);
if (n / 2 != cnt) {
cout << -1 << endl;
return;
}
dfs2(1, 0, 1);
dfs3(1, 0, 0);
for (int i = 1; i <= n; i++) {
if (id[i] == 0) cout << 'R';
else cout << 'B';
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
#endif
solve();
}
/*
10
1 2
1 4
1 6
3 2
4 5
6 7
7 8
6 9
9 10
*/