题目链接:Codeforces - The Door Problem
如果从灯泡的角度思考,比较麻烦。我们直接想开关,每个开关只有两种选择。
然后对于每个灯泡可以确定开关的选择,最后2-SAT即可。
AC代码:
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=2e5+10;
int n,m,a[N];
int low[N],dfn[N],scc[N],vis[N],co,cnt; stack<int> s;
vector<int> g[N],v[N];
char *fs,*ft,buf[1<<15];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
inline int read(){
int x=0,f=1; char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc();}
return x*f;
}
inline void add(int a,int b){g[a].push_back(b);}
void Tarjan(int x){
low[x]=dfn[x]=++cnt; s.push(x),vis[x]=1;
for(auto to:g[x]){
if(!dfn[to]) Tarjan(to),low[x]=min(low[x],low[to]);
else if(vis[to]) low[x]=min(low[x],dfn[to]);
}
if(low[x]==dfn[x]){
co++; int u;
do{
u=s.top(); s.pop(); vis[u]=0; scc[u]=co;
}while(u!=x);
}
}
signed main(){
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1,k,x;i<=m;i++){
k=read(); while(k--) x=read(),v[x].push_back(i);
}
for(int i=1,x,y;i<=n;i++){
x=v[i][0],y=v[i][1];
if(a[i]) add(x,y),add(y,x),add(x+m,y+m),add(y+m,x+m);
else add(x,y+m),add(y,x+m),add(x+m,y),add(y+m,x);
}
for(int i=1;i<=m*2;i++) if(!dfn[i]) Tarjan(i);
for(int i=1;i<=m;i++) if(scc[i]==scc[i+m]) return puts("NO");
puts("YES");
return 0;
}