题目描述
处女座进行了一次探险,发现了一批宝藏。如果他获得这批宝藏,那么他一辈子都不需要工作了。但是处女座遇到了一个难题。

宝藏被装在n个宝箱里,宝箱编号为1,2,…,n,只有所有宝箱在某一时间被打开,处女座才能获得宝藏。有m个开关,每个开关控制k个宝箱,如果按下一个开关,那么这k个宝箱的开关状态都会发生改变(从开启变成关闭,从关闭变成开启),处女座想知道他能否获得这批宝藏。

输入描述:
第一行两个正整数n,m,
第二行n个整数,每个整数为0或1,表示初始时宝箱的状态,0表示开启,1表示关闭
接下来m行,每行开头一个整数k表示这个开关控制的宝箱的个数,接下来k个整数,表示控制宝箱的编号
1<=n,m<=200000
1<=k<=n
题目保证每个宝箱最多被两个开关控制。

输出描述:
一行,如果处女座能获得宝藏,输出”YES”,否则输出”NO”
示例1
输入
复制
4 4
1 0 1 1
2 3 4
2 1 3
1 2
2 1 2
输出
复制
YES


对于每一个开关我们只有按或者不按,也就是0或1的选择。

不难看出是一道2-SAT,的问题。

然后我们分别按照要求建边即可。


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=4e5+10,M=2e6+10;
int n,m,k,dfn[N],low[N],col[N],co,cnt,a[N],vis[N];
int head[N],nex[M],to[M],tot;
vector<int> v[N]; stack<int> st;
inline void add(int a,int b){to[++tot]=b; nex[tot]=head[a]; head[a]=tot;}
void tarjan(int x){
	dfn[x]=low[x]=++cnt; vis[x]=1; st.push(x);
	for(int i=head[x];i;i=nex[i]){
		if(!dfn[to[i]]){
			tarjan(to[i]); low[x]=min(low[x],low[to[i]]);
		}else if(vis[to[i]]) low[x]=min(low[x],dfn[to[i]]);
	}
	if(dfn[x]==low[x]){
		int u; co++;
		do{
			u=st.top(); vis[u]=0; st.pop(); col[u]=co;
		}while(u!=x);
	}
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)	scanf("%d",&a[i]);
	for(int i=1,k,x;i<=m;i++){
		scanf("%d",&k);
		while(k--){
			scanf("%d",&x);	v[x].push_back(i);
		}
	}
	for(int i=1;i<=n;i++){
		if(v[i].size()==2){
			if(!a[i]){
				add(v[i][0],v[i][1]),add(v[i][1],v[i][0]);
				add(v[i][0]+m,v[i][1]+m),add(v[i][1]+m,v[i][0]+m);
			}else{
				add(v[i][0],v[i][1]+m),add(v[i][1],v[i][0]+m);
				add(v[i][0]+m,v[i][1]),add(v[i][1]+m,v[i][0]);
			}
		}else if(v[i].size()==1){
			if(!a[i]){
				add(v[i][0],v[i][0]+m);
			}else{
				add(v[i][0]+m,v[i][0]);
			}
		}else if(v[i].size()==0){
			if(a[i])	return puts("NO"),0;
		}
	}
	for(int i=1;i<=m*2;i++)	if(!dfn[i])	tarjan(i);
	for(int i=1;i<=m;i++)	if(col[i]==col[i+m])	return puts("NO"),0;
	puts("YES");
	return 0;
}