CF173D Deputies

一、题目

点此看题

二、解法

首先三个黑点、三个白点是肯定能能组队的,我们考虑白点数模 3 3 3 1 1 1的情况(黑点如此同理)

此时能够组队的方案有一白两黑,或者是 2 2 2个两白一黑,都去试一试就行了,大于 3 3 3的特殊配对(不是三个同色)肯定不用考虑,因为都能归类到上述情况中去。至于解决上述问题,贪心地找度数最小的白点 / / /黑点,再看看能不能有两个点和他们组队。

还有一个问题,就是第一个点染什么颜色会不会有影响?我们讨论连通块个数, 1 1 1个显然无影响, 2 2 2个不清楚,但是好枚举(都试试看,反正只有两种本质不同的情况), 3 3 3个及以上就需要说明。

可以通过连虚边来化为 3 3 3个联通块的情况,这样限制更多,但若任然合法则可以说明问题,来讨论大小为 1 1 1的连通块的个数(其他情况可以通过不同连通块组队化成这种情况):
在这里插入图片描述
嫖了jzm的证明…

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
using namespace std;
const int M = 100005;
const int inf = 0x3f3f3f3f;
int read()
{
    int num=0,flag=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
    return num*flag;
}
int n,m,k,cnt,tot,w,b,shit[M];
int du[M],f[M],fa[M],use[M],col[M];
struct edge
{
	int v,next;
	edge(int V=0,int N=0) : v(V) , next(N) {}
}e[2*M];
int find(int x)
{
	if(x==fa[x]) return x;
	return fa[x]=find(fa[x]);
}
void uni(int u,int v)
{
	int x=find(u),y=find(v);
	if(x==y) return;
	k--;fa[x]=y;
}
void fuck(int u,int c)
{
	if(col[u]!=-1) return ;
	col[u]=c;
	if(c==0) w++;
	else b++; 
	for(int i=f[u];i;i=e[i].next)
		fuck(e[i].v,c^1);
}
void paint(int f)
{
	w=b=0;
	memset(col,-1,sizeof col);
	for(int i=1;i<=n;i++)
		if(i==fa[i])
			fuck(i,f);
}
int work2(int fl)
{
	int mx=inf,t=0;
	for(int i=1;i<=n;i++)
		if(du[i]<mx && col[i]==fl && !use[i])
		{
			t=i;
			mx=du[i];
		}
	int x=2;
	memset(shit,0,sizeof shit);
	for(int i=f[t];i;i=e[i].next)
		if(col[e[i].v]==fl^1)
			shit[e[i].v]=1;
	for(int i=1;i<=n;i++)
		if(!shit[i] && col[i]==fl^1 && !use[i])
			x--;
	if(x>0) return 0;
	cnt++;x=2;
	use[t]=cnt;
	for(int i=1;i<=n;i++)
		if(!shit[i] && col[i]==fl^1 && !use[i])
		{
			use[i]=cnt;
			x--;if(x==0) return 1;
		}
	return 1;
}
void print()
{
	queue<int> q1,q2;
	for(int i=1;i<=n;i++)
	{
		if(!use[i] && col[i]==0)
			q1.push(i);
		if(!use[i] && col[i]==1)
			q2.push(i);
	}
	while(!q1.empty())
	{
		cnt++;
		for(int i=0;i<3;i++)
		{
			int t=q1.front();q1.pop();
			use[t]=cnt;
		}
	}
	while(!q2.empty())
	{
		cnt++;
		for(int i=0;i<3;i++)
		{
			int t=q2.front();q2.pop();
			use[t]=cnt;
		}
	}
	puts("YES");
	for(int i=1;i<=n;i++)
		printf("%d ",use[i]);
	exit(0);
}
void work()
{
	cnt=0;
	if(w%3==1)
	{
		//一白两黑
		if(work2(0))
			print();
		//两黑四白
		if(w>=4 && work2(1) && work2(1))
			print();
	}
	else
	{
		if(work2(1))
			print();
		if(b>=4 && work2(0) && work2(0))
			print();
	}
}
signed main()
{
	n=k=read();m=read();
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		e[++tot]=edge(v,f[u]),f[u]=tot;
		e[++tot]=edge(u,f[v]),f[v]=tot;
		du[u]++;du[v]++;
		uni(u,v);
	}
	paint(0);
	memset(use,0,sizeof use);
	if(w%3) work();
	else print();
	if(k==2)
	{
		int x=0;
		w=b=0;
		memset(col,-1,sizeof col);
		for(int i=1;i<=n;i++)
			if(i==fa[i])
			{
				fuck(i,x);
				x^=1;
			}
		memset(use,0,sizeof use);
		if(w%3) work();
		else print();
	}
	puts("NO");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值