BZOJ传送门
洛谷传送门
解析(含证明):
显然所有非二分图全部gg。
如果一个点不在任何环中,它就是无效的。我们删去这些点,保留剩下点的导出子图。
显然现在的图只有若干个二分图形成的联通快,我们对每个联通快单独考虑。
显然一个单独偶环或独立点可以直接丢掉。
将以上两个情况去除之后,我们发现有一些二分图也gg了,比如这个:
我们用大写字母表示颜色,可以为上面的图赋颜色集合为:
- AB
- BC
- AC
- AC
- AB
- AC
- BC
由于
7
7
7是
B
C
BC
BC,所以
5
,
6
5,6
5,6中必然会出现一个
A
A
A,对于
4
4
4来说,
A
A
A就不能选了。
由于
1
1
1是
A
B
AB
AB,所以
2
,
3
2,3
2,3中不然会出现一个
C
C
C,对于
4
4
4来说,
C
C
C就不能选了。
于是就咕咕咕了。我们发现,所有存在度数不小于 4 4 4的点的图都gg。
那剩下的图就安全了吗?也不是:
对这个图赋颜色集合如下:
- BC
- AB
- AC
- BC
- AB
- AC
自己手玩一下就会发现 3 , 6 3,6 3,6那里总是gg的。
为什么,因为两边连接两个点的路径太长导致我们有机会构造出有相同元素的集合而导致选择互相矛盾,(不要问我怎么证明,我只会口胡)
算了还是证明一下吧。现在只考虑两个偶环相交的情况,设两个交点为 u , v u,v u,v,对 u , v u,v u,v中间三条路径的长度分情况讨论。注意这里的长度一律指边数
1. 三条路径长度为奇数
这时候可以轻易卡掉,令 u u u的集合为 A C AC AC, v v v的集合为 A C AC AC,第一条路径上的集合全部为 A C AC AC,可以保证 u u u, v v v必须选择不同的颜色。
现在用一条最短的路径将 u u u选择 A A A同时 v v v选择 C C C的情况ban掉,令一种情况同理。
则剩下的路径上边数都至少为 3 3 3(不考虑重边),点数至少为 2 2 2(不含 u , v u,v u,v)。
令路径 2 2 2上与 u u u相邻的点为 w w w,令 w w w的颜色集合为 A B AB AB,路径 2 2 2上剩下的节点颜色为 B C BC BC,由于节点数为奇数,所以当 u u u选择 A A A的时候, w w w必须选择 B B B,然后 v v v相邻的节点就会选择 C C C, v v v就不可能选择 C C C了。
剩下的第三条路径同理构造,就可以卡掉 u u u选择 C C C,而 v v v选择 A A A的情况了。
2.三条路径长度为偶数,且最多有一条路径长度为 2 2 2
则我们将最短的那条路径拿出来,该路径上所有点的颜色集合和 u , v u,v u,v一起赋为 A C AC AC
则 u , v u,v u,v被卡到必须选择相同的颜色。
剩下的两条路径显然边数至少为 4 4 4,点数至少为 3 3 3。
现在考虑用两条路径分别卡掉 u , v u,v u,v选择相同颜色的情况。
还是令路径 2 2 2上与 u u u相邻的点为 w w w, w w w颜色集合为 A B AB AB,剩下节点颜色集合为 B C BC BC…….
看明白了上面奇数的证明的应该都明白这里是怎么回事,略过了。
我们发现只有当两个点分成的三条路径是2-2-偶数的时候我们才有解。
刚才讨论的还只是两个偶环相交的情况,偶环更多呢?
那么总是存在某两个偶环的子图不满足2-2-偶数(这个用用脑子都能想明白,就不证明了),可以随便卡掉。(子图不满足,加边和点之后肯定也不满足)
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
while(!isdigit(c=gc()));re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
}
using namespace IO;
cs int N=1e4+4,M=2e4+4;
int n,m;
vector<int> edge[N];
int deg[N];
inline void addedge(int u,int v){
edge[u].push_back(v);
edge[v].push_back(u);
++deg[u],++deg[v];
}
bool col[N],vis[N];
inline bool dye(int u){
vis[u]=true;
for(int re e=edge[u].size()-1,v;~e;--e){
v=edge[u][e];
if(vis[v]){
if(col[u]==col[v])return false;
}
else {
col[v]=!col[u];
if(!dye(v))return false;
}
}
return true;
}
int fa[N];
inline int getfa(int u){
while(u^fa[u])u=fa[u]=fa[fa[u]];
return u;
}
inline void merge(int u,int v){
fa[getfa(u)]=fa[getfa(v)];
}
inline bool check(vector<int> &nd){
if(nd.size()==1)return true;
int cnt3=0,x=0,y=0;
for(int re i=nd.size()-1,u;~i;--i){
u=nd[i];
if(deg[u]>3)return false;
if(deg[u]==3)++cnt3,(x==0?x=u:y=u);
}
if(cnt3>2)return false;
if(cnt3==0)return true;
if(cnt3==2){
int cnt_pos=0;
for(int re i=nd.size()-1,u,cnt;~i;--i){
u=nd[i];cnt=0;
for(int re e=edge[u].size()-1;~e;--e)if(edge[u][e]==x||edge[u][e]==y)++cnt;
cnt_pos+=(cnt==2);
}
if(cnt_pos<2)return false;
}
return true;
}
queue<int,list<int> > q;
vector<int> nd[N];
inline void solve(){
n=getint(),m=getint();
for(int re i=1;i<=m;++i)addedge(getint(),getint());
for(int re i=1;i<=n;++i){
if(!vis[i]&&!dye(i))return (void)puts("NO");
}
memset(vis+1,0,sizeof(bool)*n);
for(int re i=1;i<=n;++i)if(deg[i]<=1)q.push(i),vis[i]=true;
while(!q.empty()){
int u=q.front();
q.pop();
for(int re e=edge[u].size()-1,v;~e;--e){
v=edge[u][e];
if(!vis[v]&&1==--deg[v])q.push(v),vis[v]=true;
}
}
for(int re i=1;i<=n;++i)fa[i]=i;
for(int re i=1;i<=n;++i)if(!vis[i]){
for(int re e=edge[i].size()-1;~e;--e){
if(!vis[edge[i][e]])merge(i,edge[i][e]);
}
}
for(int re i=1;i<=n;++i)if(!vis[i])nd[getfa(i)].push_back(i);
for(int re i=1;i<=n;++i)if(nd[i].size())if(!check(nd[i]))return (void)puts("NO");
puts("YES");
}
inline void init(){
for(int re i=1;i<=n;++i)edge[i].clear(),nd[i].clear();
memset(deg+1,0,sizeof(int)*n);
memset(vis+1,0,sizeof(bool)*n);
}
int T;
signed main(){
T=getint();
while(T--){
init();
solve();
}
return 0;
}