COCI 2018/2019 Round1 Teoretičar

这是一道关于二分图染色的问题,要求使用不超过2的k次幂种颜色,使得图中每条边染色后没有相同颜色的边共用节点。题目给出点数n≤200000,边数m≤500000。初始尝试的简单策略失败后,提出了将边分割并递归处理的方法,利用欧拉回路确保每个点的度数为偶数。

Little Alan was bored so he asked Goran to give him an interesting problem. Since he’s busy with
preparing for exams, Goran could only recall one huge bipartite graph from his old days as a
programming competitor. He gave the graph to Alan and said: ​You have to colour the edges of this
bipartite graph using as few colours as possible in such a way that there are no two edges of the
same colour sharing a node.
Alan excitedly ran to his room, took out his movable read/write device for its tape and start to work on
the problem. However, he soon realized that he’s missing something so he got back to Goran and
said: ​Give me an infinite tape and I will solve your problem! Goran gave him a significant look: ​Infinite
tape? If you continue to theorize about everything, there won’t be a single thing named after you.
After seeing Alan starting to tear up, Goran decided to show mercy: ​I will make it a bit easier for you.
Let C be the smallest number of colours needed to paint the graph in the described way. I will let you
use at most X colours, where X is the lowest power of 2 not less than C.
Help Alan solve the problem.
Note ​: A bipartite graph is a graph whose nodes can be divided in two sets (or sides) in such a way
that each edge of graph connects one node from the first set with one node from the second set.

题意:一个二分图,给边染色使得共点的边不同色,且用的颜色数量小于等于2k,其中k是最小的k满足2k>=最小的满足条件的颜色数,求染色方案。
点数n<=200000,边数m<=500000
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
这个。乍一看可以乱搞,但是。。。。
首先可以想到一次给两种颜色,一直给直到结束,然后,它WA了。
可以猜到最小颜色数为最大点度数,但是直接染是O(n^2)的。
考虑把这些边分为两个部分,使得每个点的度数都尽量被这两个部分平分。
然后递归处理。
边的分割用欧拉回路(适当加虚点和虚边使得每个点的度数都为偶数)
每次看外国网友的代码都觉得他们太依赖STL了(还有就是代码又长又丑又易懂)。
所以经常打CF会得STL依赖症?

AC Code:

#include<bits/stdc++.h>
#define maxn 200005
#define maxm 1000005
using namespace std;

int m,n,l,r;
struct node{ int u,v; };
vector<node>edge,G;
vector<int>g[maxn];
int vis[maxm],tim,col[maxm],in[maxn];
int cl=0;

void dfs(int now)
{
	for(;g[now].size();)
	{
		int v = g[now].back();
		g[now].pop_back();
		if(vis[v]==tim) continue;
		col[v] = (cl^=1);
		vis[v] = tim;
		dfs(G[v].u == now ? G[v].v : G[v].u);
	}
}

vector<int>solve(vector<node>e)
{
	if(!e.size())
	{
		return {};
	}
	++tim;
	G=e;
	int cnt_e = e.size() , siz = cnt_e;
	vector<int>pt;
	for(int i=0;i<cnt_e;i++)
	{
		if(vis[e[i].u]!=tim) vis[e[i].u]=tim,g[e[i].u].clear(),in[e[i].u]=0,pt.push_back(e[i].u);
		if(vis[e[i].v]!=tim) vis[e[i].v]=tim,g[e[i].v].clear(),in[e[i].v]=0,pt.push_back(e[i].v);
		in[e[i].u]++,in[e[i].v]++;
		g[e[i].u].push_back(i);
		g[e[i].v].push_back(i);
	} 
	bool flag=0;
	for(vector<int>::iterator it = pt.begin();it!=pt.end();it++)
		flag |= g[*it].size() > 1;
	if(!flag)
	{
		vector<int>ret;
		for(int i=0;i<siz;i++) ret.push_back(1);
		return ret; 
	}
	in[n+1] = 0;
	for(vector<int>::iterator it = pt.begin();it!=pt.end();it++)
		if(in[(*it)] & 1) 
			if((*it)<=l)
			{
				g[n+2].push_back(cnt_e),
				in[n+2]++;
				g[*it].push_back(cnt_e++),
				G.push_back(node{*it,n+2});
			}
			else
			{
				g[n+1].push_back(cnt_e),in[n+1]++,
				g[*it].push_back(cnt_e++);
				G.push_back(node{n+1,*it});
			}
	if(in[n+1] & 1) 
		g[n+2].push_back(cnt_e),
		g[n+1].push_back(cnt_e++),
		G.push_back(node{n+1,n+2});
	++tim;
	for(vector<int>::iterator it = pt.begin();it!=pt.end();it++)
		dfs(*it);
	vector<node>tx,ty;
	vector<int>ccl;
	for(int i=0;i<siz;i++)
	{
		if(col[i]) ty.push_back(e[i]);
		else tx.push_back(e[i]);
		ccl.push_back(col[i]);
	}
	vector<int>rx=solve(tx),ry=solve(ty);
	int hx = *max_element(rx.begin(),rx.end()) , px=0,py=0;
	vector<int>ret;
	for(int i=0;i<siz;i++)
		if(ccl[i]) ret.push_back(ry[py++] + hx);
		else ret.push_back(rx[px++]);
	return ret;
}

int main()
{
	scanf("%d%d%d",&l,&r,&m),n=l+r;
	for(int i=1,u,v;i<=m;i++)
		scanf("%d%d",&u,&v),
		edge.push_back(node{u,v+l});
	vector<int>ans = solve(edge);
	printf("%d\n",*max_element(ans.begin(),ans.end()));
	for(int i=0;i<m;i++)
		printf("%d\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值