原题链接
https://ac.nowcoder.com/acm/contest/11259/C
原题大意
有一个 n(3≤n≤3 ⋅105)\ n(3\leq n\leq 3\ ·10^5) n(3≤n≤3 ⋅105)个点 m(3≤5 ⋅105)\ m(3\leq 5\ ·10^5) m(3≤5 ⋅105)条边的简单无向图,需要将每个顶点染为红绿蓝三种颜色之一。然后对于每条边,若其两个端点颜色一样,则边也变成对应颜色,否则保持黑色。
基础目标为:
- 仅有黑边的情况下,图依旧保持联通。
在完成基础目标的前提下,还可以完成以下额外目标:
1.每种颜色顶点数相同。(保证 nnn 是 333 的倍数)
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}G非叶≤R≤3n,G叶>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("");
}
}