Codeforce 547 D. Mike and Fish(欧拉回路 + 思维 + 构造)

北战南征 提交于 2020-01-22 19:36:22

在这里插入图片描述


题目大意:二维平面上有 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,每条边也可以被访问多次,最差还是能卡到 n2n^2

具体见代码:


代码:

#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;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!