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;
}