题意
要求构造一个序列,包含 \(0,1,2,3\),相邻的两个数不能是 00,11,22,33,02,20,23,32,13,31,并且有 \(m\) 个限制,每个限制给出一些位置,要求这些位置上的数两两不同,问有没有解。
\(n\le10^5,m\le5000\)。
思路
显然,当一个约束集合的大小 \(>4\) 时无解。
发现 \(3\) 旁边能放 \(0\),\(0\) 旁边能放 \(1,3\),\(1\) 旁边能放 \(0,2\),\(2\) 旁边能放 \(1\)。给数字重标号,那么问题变成了,判断是否有一个由 \(\set{1,2,3,4}\) 组成的序列,相邻两个元素相差 \(1\),有一些位置数不能相同。
把位置按照奇偶性分成两部分,令第一个数是 \(1\) 或 \(3\),那么奇数位置的数都是 \(1\) 或 \(3\),偶数位置的数都是 \(2\) 或 \(4\)。把每个约束拆成很多个二元的约束,用 \(2-sat\) 解决。一个约束对 \((u,v)(u<v)\),如果 \(v-u\) 是奇数,那么祂们肯定不一样,否则转换为一些推导关系。相邻的两个数之间也有推导关系。
代码
// Problem: P3876 [TJOI2010] 数字序列
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3876
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)#include<bits/stdc++.h>
using namespace std;
namespace IO{template<typename T>inline void read(T&x){x=0;char c=getchar();bool f=0;while(!isdigit(c)) c=='-'?f=1:0,c=getchar();while(isdigit(c)) x=x*10+c-'0',c=getchar();f?x=-x:0;}template<typename T>inline void write(T x){if(x==0){putchar('0');return ;}x<0?x=-x,putchar('-'):0;short st[50],top=0;while(x) st[++top]=x%10,x/=10;while(top) putchar(st[top--]+'0');}inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}inline void write(char c){putchar(c);}inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
const int maxn=100010;
int n,m,p[maxn],cnt_scc,sy[maxn*2];
vector<int>e[maxn*2],f[maxn*2],vt;
bool flag[maxn*2];
int id_1(int u){return u*2-1;}
int id_2(int u){return u*2;}
void edge_add(int u,int v){e[u].push_back(v),f[v].push_back(u);}
void dfs1(int u){if(flag[u]) return ;flag[u]=1;for(int v:f[u]) dfs1(v);vt.push_back(u);
}
void dfs2(int u,int bh){if(sy[u]) return ;sy[u]=bh;for(int v:e[u]) dfs2(v,bh);
}
void qlt(){memset(flag,0,sizeof(flag));memset(sy,0,sizeof(sy));cnt_scc=0;for(int i=1;i<=n*2;i++) dfs1(i);while(!vt.empty()) dfs2(vt.back(),++cnt_scc),vt.pop_back();
}
void solve(){for(int i=1;i<=n*2;i++) e[i].clear(),f[i].clear();read(n,m);bool f=0;for(int i=1;i<=m;i++){int L;read(L);for(int j=1;j<=L;j++) read(p[j]);if(L>4){f=1;continue;}for(int a=1;a<=L;a++) for(int b=a+1;b<=L;b++){int u=p[a],v=p[b];if((u&1)!=(v&1)) continue;edge_add(id_1(u),id_2(v));edge_add(id_2(u),id_1(v));edge_add(id_1(v),id_2(u));edge_add(id_2(v),id_1(u));}}for(int i=1;i<n;i++){edge_add(id_2(i),id_1(i+1));edge_add(id_2(i+1),id_1(i));}if(f){write("No");return ;}qlt();for(int i=1;i<=n;i++) if(sy[id_1(i)]==sy[id_2(i)]){write("No");return ;}write("Yes");
}
signed main(){int T;read(T);while(T--) solve(),write("\n");return 0;
}
