2021牛客多校#8 C-Fuzzy Graph

本文解析了一道关于给定无向图的染色问题,要求在保持联通性和等色节点数量的同时,最大化一种颜色节点的数量且其相邻边为黑色。解题策略涉及构造树、贪心策略及颜色调整。详细介绍了如何通过DFS实现初步染色和优化方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题链接
				https://ac.nowcoder.com/acm/contest/11259/C

原题大意

有一个 n(3≤n≤3 ⋅105)\ n(3\leq n\leq 3\ ·10^5) n(3n3 105)个点 m(3≤5 ⋅105)\ m(3\leq 5\ ·10^5) m(35 105)条边的简单无向图,需要将每个顶点染为红绿蓝三种颜色之一。然后对于每条边,若其两个端点颜色一样,则边也变成对应颜色,否则保持黑色。
基础目标为:

  • 仅有黑边的情况下,图依旧保持联通。

在完成基础目标的前提下,还可以完成以下额外目标:
1.每种颜色顶点数相同。(保证 nnn333 的倍数)
2.某种颜色的顶点数最多,但该颜色的节点的相邻边都是黑色。这个额外目标可以达成多次。
如下所示,左图是染色前,右图是按某种方式染色后的结果,其满足基础目标,每种颜色的顶点数相同,绿色节点出现次数最多且相邻边均为黑色,因此满足两个额外条件。
在这里插入图片描述

给定未染色的图,求至少满足一个额外目标的方案,或判断无解。

题解

首先我们从原图中提出一棵树,并把每一层都染成相同的颜色,相邻的层颜色不同,完成初步染色。

接下来,分两种情况进行讨论:
①此时初步染色的两种颜色的节点数都大于n3\frac{n}{3}3n,我们只需要把多余的点全部染成另一种颜色即可。染色时需考虑其父(或子)节点是否已经染色,我们只需要从叶节点向上进行贪心,找到一个可以染色的点,将其染色,并把它的父节点进行标记,表示无法染色。此时得到的树满足要求一。

②有一种颜色的节点数小于n3\frac{n}{3}3n,我们设节点数较多的颜色为GGG,较少的为RRR。则可以得到:
G叶+G非叶>2n3,R叶+R非叶<n3G_{叶}+G_{非叶}>\frac{2n}{3},R_{叶}+R_{非叶}<\frac{n}{3}G+G>32n,R+R<3n。因为在我们找树并初步染色时得到的树保证每个GGG节点有一个RRR节点,所以我们又可得G非叶≤R≤n3,G叶>n3G_{非叶}\le R\le \frac{n}{3},G_{叶}>\frac{n}{3}GR3nG>3n。因为初始dfsdfsdfs找树时满足叶节点之间相互独立,所以将G叶G_{叶}G染成另一种未染过的颜色,所以将所有G叶G_{叶}G染成其他颜色的节点数为所有颜色中最多且相邻节点无直接连边(即所有G叶与相邻节点的颜色为黑色G_{叶}与相邻节点的颜色为黑色G),满足要求二。

参考代码

#include<bits/stdc++.h>
#define FOR(i,n,m) for(int i=n;i<=m;i++)
#define For(i,n,m) for(int i=n;i>=m;i--)
#define pb push_back
using namespace std;
const int N=3e5+5;
int T,n,m;
int col[N],f[N],num[3],leaf[N],cnt,o[N];
vector<int> e[N];
char color[3]={'G','B','R'};
void dfs1(int x,int fa,int d)
{
	int flag=0;
	f[x]=fa;
	col[x]=d;//染色
	num[d]++;//染色
	o[x]=1;
	FOR(i,0,e[x].size()-1)
	{
		int son=e[x][i];
		if(o[son]==1||son==fa)continue;
		flag=1;dfs1(son,x,d^1);
	}
	if(!flag)leaf[++cnt]=x;//记录叶节点,方便进行操作二的染色
}
void dfs2(int x)
{
	int flag=0;
	o[x]=0;
	FOR(i,0,e[x].size()-1)
	{
		int son=e[x][i];
		if(o[son]==0)continue;
		dfs2(son);
		if(col[son]==2)flag=1;
	}
	if(flag==0&&num[col[x]]>n/3)//如果找到一个可以染色的节点,直接染色
	{
		num[col[x]]--;
		col[x]=2;
		num[2]++;
	}
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		cnt=0;
		num[0]=num[1]=num[2]=0;
		FOR(i,1,n){e[i].clear();col[i]=0;f[i]=0;leaf[i]=0;o[i]=0;}
		FOR(i,1,m)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			e[x].pb(y);e[y].pb(x);
		}
		dfs1(1,-1,1);//dfs找树
		if(num[0]>=n/3&&num[1]>=n/3)
			dfs2(1);
		else
			FOR(i,1,cnt)
				col[leaf[i]]=2;
		FOR(i,1,n)printf("%c",color[col[i]]);
		puts("");
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值