luoguP4429 [BJOI2018]染色

一个无向图,每个点设置一个大小为\(2\)的颜色集合,表示这个点可以选择两种颜色其中之一。

给你这张无向图,问是否对于设置集合的任意方案,都可以染色使得每条边连接的两个点颜色不同。

\(n\le 10^4,m\le 2*10^4\)


奇怪的结论题/构造题。然而官方数据好水啊真不知道那年bj人是怎么挺过来的。洛谷上加了hack数据,这是我标题放了洛谷的原因。

对于每个连通块分别处理。显然如果图不是二分图就NO。然后去掉所有度数小于等于\(1\)的点。

考虑使用三种颜色或四种颜色(四种颜色的情况比较少)。(经过下面的分析得到筛剩下的情况中用更多的颜色没有什么卵用。)

发现结论1:如果存在偶环,那么可以用一种构造方案,使得块中的某个点选择一种颜色时一定不合法(或者说强制选另一种颜色)。

可以考虑\(z\to y\)经过偶环且\(y\)在偶环上。设\(x\)为路径上的第一个偶环上的点。此时\(x\to y\)有两条路径。不妨设\(x\)\((A,B)\)(它的状态可以直接被\(z\)钦定),当选\(A\)时,\(x\)的两个后继都是固定的颜色,路径上的每个点的颜色都被前一个点决定;一直到\(B\)的前驱,发现两个前驱选的颜色互不相同而且也是\(B\)的集合中的颜色。所以\(x\)\(A\)不合法,但此时并不能同时决定选\(B\)不合法。

推论1:如果存在两个不在边相交的偶环,那么一定不合法。

可以找到中间的一个点\(z\)。如果两个环只有一个交点,构造方案钦定\(z\)\(A\)时环甲不合法,选\(B\)时环乙不合法。如果环有两个交点,具体是说两个偶环上的\(y\)重合,此时类似,但需要用到四种颜色。

结论2:考虑两个相交的偶环,找到两个三度点\(u,v\)。此时\(u\to v\)有三条不相交的路径,当且仅当路径长度分别为\(2,2,偶数\)时合法。

其实只需要证明\(1,3,3\)不合法,和\(2,4,4\)不合法。更长的情况相当于在某个路径上加偶数个点,可以钦定一下使得经过了偶数个点之后状态不变。

先证前者。比如下面这个图(四相邻格子有边),如此构造即可。

\((B,C)\) \((A,B)\)
\((A,C)\) \((A,C)\)
\((A,B)\) \((B,C)\)

再证后者。比如下面(除了第二行没有横边外四相邻有边)。

\((A,C)\) \((A,B)\) \((A,B)\)
\((B,C)\) (没横边)\((A,B)\)(没横边) \((A,C)\)
\((A,B)\) \((A,B)\) \((B,C)\)

推论2:\(m\ge n+2\)时一定不合法,\(m\le n\)时一定合法。\(m=n+1\)时需要用上面条件判断。

只用证\(m=n+2\),如果所有的环两两相交时,一定存在不满足\(2+2+偶数\)的子图。具体考虑从\(m=n+1\)的合法情况中加入一条边,那么相当于在\(2+2+偶数\)的子图中加入一条路径(长度任意)连接其中两个点。讨论一下加入的路径连接哪里(此处省略具体过程),最终证明出都不合法。

按照以上的结论判断即可。


using namespace std;
#include <bits/stdc++.h>
#define N 100005
#define M 200005
int n,m;
struct EDGE{
	int to;
	EDGE *las;
} e[M*2];
int ne;
EDGE *last[N];
void link(int u,int v){
	e[ne]={v,last[u]};
	last[u]=e+ne++;
}
int ans;
int c[N];
int cntd,cnte;
int q[N];
void color(int x){
	q[cntd++]=x;
	for (EDGE *ei=last[x];ei;ei=ei->las,cnte++)
		if (c[ei->to]==-1)
			c[ei->to]=c[x]^1,color(ei->to);
		else if (c[x]^c[ei->to]^1)
			ans=0;
}
int deg[N];
void work(){
	static queue<int> que;
	for (int i=0;i<cntd;++i){
		deg[q[i]]=0;
		for (EDGE *ei=last[q[i]];ei;ei=ei->las)
			deg[q[i]]++;
		if (deg[q[i]]<=1)
			que.push(q[i]);
	}
	while (!que.empty()){
		int x=que.front();
		que.pop();
		for (EDGE *ei=last[x];ei;ei=ei->las)
			if (--deg[ei->to]==1)
				que.push(ei->to);
	}
	int nei[2][3],k[2]={0,0},c=0;
	for (int i=0;i<cntd;++i){
		if (deg[q[i]]==4){
			ans=0;
			return;
		}
		if (deg[q[i]]==3){
			for (EDGE *ei=last[q[i]];ei;ei=ei->las)
				if (deg[ei->to]>1)
					nei[c][k[c]++]=ei->to;
			assert(k[c]==3);
			sort(nei[c],nei[c]+3);
			c++;
		}
	}
	assert(c==2);
	int i=0,j=0,cnt=0;
	while (i<k[0] && j<k[1])
		if (nei[0][i]<nei[1][j])
			i++;
		else if (nei[0][i]>nei[1][j])
			j++;
		else
			i++,j++,cnt++;
	ans&=(cnt>=2);
}
int main(){
	freopen("in.txt","r",stdin);
	int T;
	scanf("%d",&T);
	while (T--){
		scanf("%d%d",&n,&m);
		memset(last,0,sizeof(EDGE*)*(n+1));
		ne=0;
		for (int i=1;i<=m;++i){
			int u,v;
			scanf("%d%d",&u,&v);
			link(u,v);
			link(v,u);
		}
		memset(c,255,sizeof(int)*(n+1));
		ans=1;
		for (int i=1;i<=n && ans;++i)
			if (c[i]==-1){
				cntd=cnte=0;
				c[i]=0,color(i);
				cnte/=2;
				if (cnte>=cntd+2)
					ans=0;
				else if (cnte==cntd+1)
					work();
			}
		printf("%s\n",ans?"YES":"NO");
	}
	return 0;
}
posted @ 2021-01-14 12:29  jz_597  阅读(137)  评论(0编辑  收藏  举报