题目大意:二维平面上有 n 个点,对这 n 个点进行黑白染色,使得每行每列都满足 黑色的点和白色的点的个数的差值 小于等于 1.
按行列连边构成一张二分图,X部为 x 坐标, Y部为 y 坐标,一个点的坐标为 (x,y),则连一条从 x 到 y 的边。
问题转化为对边进行染色,使得任意一个点所连的边中,黑色和白色的差值的绝对值不超过 1。
每一个点具有偶数度是无向图具有欧拉回路的充分必要条件,若在二分图上满足这个重复必要条件,则可以在这个二分图上构造一条欧拉回路,每条(x,y)边作为入边时染成黑色,作为出边时染成白色。
这样构造一定满足条件,每一个节点连出去的边一定有一半被染成黑色,一半被染成白色。
对于二分图上奇度数的节点,分别在 X 部和 Y部 构造一个虚点,将它们分别连一条边到虚点上,使得最后可以构造欧拉回路。
可以观察到 X 部奇度数的点的个数的奇偶性,和Y部的相同,使用归纳法证明:
当只有一条边时:显然成立,X 部和 Y 部各有一个节点具有奇度数。
假设二分图有 n 条边时,结论也成立。
当 n = n + 1 时,新加的边必然使得X部和Y部的 奇度数的点的个数的奇偶性同时改变
因此可以证得两边具有奇度数的点的个数的奇偶性一定相同,基于这个性质,为 X,Y分别构建一个虚点,奇度数的点向虚点连边,最后一定能构造出一个具有欧拉回路的二分图。
关于实现:
构造欧拉回路使用 圈套圈算法
(就是暴力):任选一个起点,从起点开始 dfs 遍历,对每条走过的边打标记,当一个点再无其它边可走,回溯时将这个点压入栈中,最后栈中节点的逆序就是欧拉回路0。
欧拉回路考虑的是每条边只经过一次,但每个点能经过多次,已经经过的边如果下次访问到这个点时又被考虑,那么这条边会被访问多次(判断它能不能走),会被T爆,需要删边。
使用前向星实现时,只需要每次更改头指针 head 即可,遍历的条件也要改成 i = head[u](而不是原来的 i = nxt[i],否则即使更改了 head,每条边也可以被访问多次,最差还是能卡到 )
具体见代码:
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
const int N = 2e5;
#define pii pair<int,int>
#define fir first
#define sec second
map<pii,int> mp;
int n,vis[maxn << 2],use[maxn << 2],d[maxn << 2],ans[maxn << 2];
int head[maxn << 2],to[maxn << 2],id[maxn << 2],nxt[maxn << 2],cnt;
vector<int> sta,X,Y;
void add(int u,int v,int p) {
to[cnt] = v;
nxt[cnt] = head[u];
id[cnt] = p;
head[u] = cnt++;
to[cnt] = u;
nxt[cnt] = head[v];
id[cnt] = p;
head[v] = cnt++;
}
void dfs(int u) {
use[u] = 1;
for(int i = head[u]; i + 1; i = head[u]) { //这个地方必须更改,不然一条边会访问多次
head[u] = nxt[i];
if(vis[id[i]]) continue;
vis[id[i]] = 1;
dfs(to[i]);
}
sta.push_back(u);
}
int main() {
scanf("%d",&n);
int p = -1;
memset(head,-1,sizeof head);
for(int i = 1,x,y; i <= n; i++) {
scanf("%d%d",&x,&y);
y += N;
d[x] ^= 1, d[y] ^= 1;
mp[pii(x,y)] = i;
add(x,y,i);
X.push_back(x);
Y.push_back(y);
}
int cnt = 0,s = 0,t = 3 * N + 1;
for(auto it : X) {
if(d[it]) {
cnt++;
add(it,t,cnt + n);
d[it] ^= 1,d[t] ^= 1;
}
}
for(auto it : Y) {
if(d[it]) {
cnt++;
add(it,s,cnt + n);
d[s] ^= 1, d[it] ^= 1;
}
}
if(d[s]) {
cnt++;
add(s,t,cnt + n);
d[s] ^= 1, d[t] ^= 1;
}
for(auto it : X)
if(!use[it]) dfs(it);
int color = 0;
for(int i = sta.size() - 2; i >= 0; i--) {
int u = sta[i + 1],v = sta[i];
if(u > v) swap(u,v);
if(!mp.count(pii(u,v))) {
color ^= 1;
continue;
}
ans[mp[pii(u,v)]] = color;
color ^= 1;
}
for(int i = 1; i <= n; i++) {
if(ans[i] == 0) putchar('b');
else putchar('r');
}
putchar('\n');
return 0;
}
来源:CSDN
作者:猝死在学ACM的路上
链接:https://blog.csdn.net/qq_41997978/article/details/104071483