#include <bits/stdc++.h>
using namespace std;
const int DELTA = 200000 + 2; // 因为坐标最大200000,所以用200000+2作为偏移量
int n; // 点的数量
int deg[DELTA * 2 + 5]; // 度数数组,存储每个节点的度数(包括行节点和列节点)
int hd[DELTA * 2 + 5]; // 链式前向星头指针数组
int to[DELTA * 6 + 5]; // 边的终点数组
int nxt[DELTA * 6 + 5]; // 下一条边指针数组
int ec = 1; // 边计数器,从1开始(因为要用到异或操作获取反向边)
// 添加无向边
void adde(int u, int v) {
to[++ec] = v;
nxt[ec] = hd[u];
hd[u] = ec;
// 无向图,添加双向边
// 注意:这里只添加了一次,调用时需要调用两次adde(u,v)和adde(v,u)
}
int vis[DELTA * 3 + 5]; // 边访问标记数组,记录每条边的染色情况
// DFS遍历进行欧拉回路/路径的边定向
void dfs(int x) {
// 使用当前弧优化:直接修改hd[x],避免重复遍历已访问的边
for (int &e = hd[x]; e; e = nxt[e])
// 如果这条边还没有被染色
if (!vis[e >> 1]) {
// 标记这条边已访问,并根据当前节点类型决定染色
// e>>1 得到的是原始无向边的编号(因为每条无向边对应两条有向边)
// 如果x是行节点(编号<=DELTA),染成1,否则染成2
vis[e >> 1] = 1 + (x <= DELTA);
// 继续DFS遍历
dfs(to[e]);
}
}
int main() {
scanf("%d", &n);
// 初始化头指针数组为0
memset(hd, 0, sizeof(hd));
// 初始化度数数组为0
memset(deg, 0, sizeof(deg));
// 初始化访问标记数组为0
memset(vis, 0, sizeof(vis));
// 构建二分图:行节点编号为x,列节点编号为y+DELTA
for (int i = 1, x, y; i <= n; i++) {
scanf("%d%d", &x, &y);
// 增加行节点和列节点的度数
++deg[x];
++deg[y + DELTA];
// 添加无向边:行节点x <-> 列节点y+DELTA
adde(x, y + DELTA);
adde(y + DELTA, x);
}
// 处理奇度数节点:将所有奇度数节点与虚节点0连接
for (int i = 1; i <= DELTA * 2; i++)
if (deg[i] & 1) { // 如果度数为奇数
adde(0, i);
adde(i, 0);
}
// 对每个行节点进行DFS,构建欧拉回路
for (int i = 1; i <= DELTA; i++)
dfs(i);
// 输出结果:根据边的染色情况输出对应的颜色
for (int i = 1; i <= n; i++)
// vis[i] == 1 对应行->列,输出'r'(红色)
// vis[i] == 2 对应列->行,输出'b'(蓝色)
putchar((vis[i] == 1) ? 'r' : 'b');
return 0;
}
CF547D Mike and Fish
最新推荐文章于 2025-10-25 22:04:32 发布

10万+

被折叠的 条评论
为什么被折叠?



