图论 CF547D&QBXT Test Ⅰ T2 Mike and Fish

题意:给出平面上 n n n 个点,你要把每个点染成黑色或白色。要求染完之后,任意一条与坐标轴平行的直线上,黑白点数量差的绝对值小于等于 1 1 1

这题QBXT曾经讲过,考试遇到原题依旧挂了QAQ。
全网都是二分图欧拉路的做法,在这里介绍一种新的算法,思路来自于无敌的cze,首先是建图,大家都是一个点横纵坐标之间连边,这里考虑一种新的连边方式,我们让同一行之间的两点配对,同一列之间两点配对,如果遇到奇数就单独放在那里就不用管,连出来大概是这样的:
在这里插入图片描述

然后对于每一个联通块进行黑白染色即可,感性理解一下是正确的,因为同一行同一列之间有连边且黑白染色使得相连的两边颜色不同,如果哪位大神知道怎么证明的,请告诉我,我完善一下。另外坐标需要离散化。

#include<bits/stdc++.h>
using namespace std;
struct node
{
	int next,to;
}e[401000];
int num,head[401000],n,x[401000],y[401000],c[401000];
vector<int> v1,v2;
vector<int> vx[401000],vy[401000];
void add(int from,int to)
{
	e[++num].next=head[from];
	e[num].to=to;
	head[from]=num;
}
void dfs(int x,int co)
{
	c[x]=co;
	for(int i=head[x];i;i=e[i].next)
	{
		int v=e[i].to;
		if(c[v]!=-1)
			continue;
		dfs(v,1-co);
	}
}
int main()
{
	memset(c,-1,sizeof(c));
	cin>>n;
	for(int i=1;i<=n;++i)
	{
		scanf("%d%d",&x[i],&y[i]);
		v1.push_back(x[i]);
		v2.push_back(y[i]);
	}
	sort(v1.begin(),v1.end());
	sort(v2.begin(),v2.end());
	v1.erase(unique(v1.begin(),v1.end()),v1.end());
	v2.erase(unique(v2.begin(),v2.end()),v2.end());
	for(int i=1;i<=n;++i)
	{
		x[i]=lower_bound(v1.begin(),v1.end(),x[i])-v1.begin()+1;
		y[i]=lower_bound(v2.begin(),v2.end(),y[i])-v2.begin()+1;
		vx[x[i]].push_back(i);
		vy[y[i]].push_back(i);
	}
	int sizx=v1.size();
	int sizy=v2.size();
	for(int i=1;i<=sizx;i++)
		for(int j=1;j<vx[i].size();j+=2)
		{
			add(vx[i][j],vx[i][j-1]);
			add(vx[i][j-1],vx[i][j]);
		}	
	for(int i=1;i<=sizy;i++)
		for(int j=1;j<vy[i].size();j+=2)
		{
			add(vy[i][j],vy[i][j-1]);
			add(vy[i][j-1],vy[i][j]);
		}	
	for(int i=1;i<=n;++i)
		if(c[i]<0)
			dfs(i,0);
	for(int i=1;i<=n;++i)
	{
		if(c[i])
			printf("r");
		else
			printf("b");	
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值