(巧妙转化)欧拉回路 - Mike and Fish(CF547D )

传送门


Analysis

有点妙嘞。好题?
每一行,每一列分别看作一个点
同一个点的行列相连边
显然会得到一个二分图
而对于染色操作,等价于给每条边定向
最后要求满足每个点的出入度之差<=1
(是不是和今天这道考试题WOJ#4761有点像呢?只是变简单了,只有边权为1的情况)

这个情况怎么分配来满足呢?
考虑到欧拉回路的性质
每个点的出度==入度
我们就将原图变作欧拉回路,跑一跑即可
(也就是将度数为奇数的点,两两相连。)
注意:即使增加了这些边后,新图可能还是不联通,所以我们需要将每一个点代进去算一遍
(毕竟我们只是选择了相邻的两点连接)

正确性?
由于度数为奇数的点只可能有偶数个,所以我们相邻两个连接在一起
每个点最多被增加一条边
在跑完欧拉回路后,删去这些添加的边,仍然可以满足题目限制

为什么只有偶数个呢?
考虑每增加一条边,总度数+=2.所以总度数始终为偶数


Code
#include<bits/stdc++.h>
#define in read()
#define re register
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<1)+(res<<3)+(ch^48);
		ch=getchar();
	}
	return f==1?res:-res;
} 
const int N=2e5+10;
int n,du[N<<2];
int nxt[N<<2],head[N<<2],to[N<<2],ecnt=1;
inline void add(int x,int y){
	nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;
}
int vis[N<<2];
void dfs(int u){
	for(re int &e=head[u];e;e=nxt[e]){
		int v=to[e];
		if(vis[e]||vis[e^1]) continue;
		vis[e]=1;dfs(v);
	}
}
int main(){
	n=in;
	for(re int i=1;i<=n;++i){
		int x=in,y=in;y+=2e5;
		add(x,y);add(y,x);
		du[x]++;du[y]++;
	}
	int last=0;
	for(re int i=1;i<=4e5;++i){
		if(du[i]&1){
			if(last) add(last,i),add(i,last),last=0;
			else last=i;
		}
	}
	//
	for(re int i=1;i<=4e5;++i) if(du[i])dfs(i);
	for(re int i=1;i<=n;++i) {
		if(vis[i<<1]) printf("%c",'b');
		else printf("%c",'r');
	}
	return 0;
} 

然后如果用今天T3的方法也照样A

#include <bits/stdc++.h>

template <typename _tp> inline void read(_tp&x){
	char ch=getchar(),ob=0;x=0;
	while(ch!='-'&&!isdigit(ch))ch=getchar();if(ch=='-')ob=1,ch=getchar();
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();if(ob)x=-x;
}

const int N = 4e5, M = N + N;
int dir[2][N];
int rev[M], ans[M], rep[M];
int vis[N];
int n,tot;

struct node {
	int id, to;
}a[2][N];

void add(int x, int y, int id, int t) {
	node*f = a[t];
	int*d = dir[t];
	if(f[x].id and f[x].to == y) {
		rep[f[x].id] = rep[id] = 0;
		ans[id] = 1, ans[f[x].id] = d[y];
		f[x].id = f[y].id = 0;
		return ;
	}
	bool flg = true;
	if(f[x].id) {
		flg = false;
		rep[f[x].id] = tot;
		rev[f[x].id] = d[x];
		f[x].id = 0;
		x = f[x].to;
	}
	if(f[y].id) {
		flg = false;
		rep[f[y].id] = tot;
		rev[f[y].id] = d[y] ^ 1;
		f[y].id = 0;
		y = f[y].to;
	}
	f[x].to = y, f[y].to = x;
	d[x] = 1, d[y] = 0;
	if(flg) f[x].id = f[y].id = id;
	else rep[id] = f[x].id = f[y].id = tot++;
}

void work(int p) {
	int x = p, t = 0;
	vis[x] = 1;
	while(a[t][x].id) {
		ans[a[t][x].id] = dir[t][x];
		if(vis[a[t][x].to]) return ;
		vis[a[t][x].to] = true;
		x = a[t][x].to, t ^= 1;
	}
	x = p, t = 1;
	while(a[t][x].id) {
		ans[a[t][x].id] = dir[t][x] ^ 1;
		vis[a[t][x].to] = true;
		x = a[t][x].to, t ^= 1;
	}
}

int main() {
	read(n);tot=n+1;
	for(int i=1,x,y,z;i<=n;++i) {
		read(x);read(y);y+=2e5;
		add(x, y, i,0);
	}
	for(int i=1;i<=4e5;++i)
		if(!vis[i]) work(i);
	for(int i=tot;i;--i)
		if(rep[i]) ans[i] = ans[rep[i]] ^ rev[i];
	for(int i=1;i<=n;++i){
		if(ans[i]) printf("%c",'b');
		else printf("%c",'r');
	}
		
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值