【题目描述】
处女座进行了一次探险,发现了一批宝藏。如果他获得这批宝藏,那么他一辈子都不需要工作了。但是处女座遇到了一个难题。
宝藏被装在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
思路:2-SAT
每个开关有两种状态,按或不按,并且两者一旦选择其中一种,那么就一定要选择其他开关的状态使得宝藏开启,可以发现,宝藏其实是用来建边的
由于每个锁最多被 k 个开关控制,因此可以对没有控制的锁直接进行判断:
- 被一个开关控制的锁:取 1 或取 0
- 被两个开关控制的锁:取 00 或取 11 或取 01 或取 10
根据 m 个限制条件进行建图,建图完成后 Tarjan 缩点,然后判断两个对立的点是否在同一个块中,若在同一个块则无解,反之则有解
【源代码】
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#define PI acos(-1.0)
#define E 1e-6
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define N 1000001
#define LL long long
using namespace std;
struct Edge{
int to,next;
}edge[N];
int head[N],cnt;
int n,m;
int a[N];
vector<int> G[N];
int low[N],dfn[N],belong[N];
int time_block;
int scc;
int Stack[N],top;
bool vis[N];
int num[N];
void Tarjan(int u){
low[u]=dfn[u]=++time_block;
Stack[top++]=u;
vis[u]=true;
int v;
for(int i=head[u];i != -1;i = edge[i].next){
v=edge[i].to;
if(!dfn[v]){
Tarjan(v);
if(low[u]>low[v])
low[u]=low[v];
}
else if(vis[v]&&low[u]>dfn[v])
low[u]=dfn[v];
}
if(low[u]==dfn[u]){
scc++;
do{
v=Stack[--top];
vis[v]=false;
belong[v]=scc;
num[scc]++;
}
while(v!=u);
}
}
void addEdge(int next,int to){//添边
edge[cnt].to=to;
edge[cnt].next=head[next];
head[next]=cnt++;
}
int main(){
memset(head,-1,sizeof(head));
cnt=0;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=m;i++){
int k;
cin>>k;
for(int j=1;j<=k;j++){
int x;
cin>>x;
G[x].push_back(i);
}
}
for(int i=1;i<=n;i++){
int len=G[i].size();
if(len==0){//初始条件
if(a[i]==1){
addEdge(0,0);
addEdge(1,1);
}
}
else if(len==1){//被一个锁控制
int temp=G[i][0]<<1;
if(a[i]==1)
addEdge(temp,temp^1);
else
addEdge(temp^1,temp);
}
else if(len==2){//被两个锁控制
int x=G[i][0]<<1;
int y=G[i][1]<<1;
if(a[i]==1){
addEdge(x,y^1);
addEdge(y,x^1);
addEdge(x^1,y);
addEdge(y^1,x);
}
else{
addEdge(x,y);
addEdge(y,x);
addEdge(x^1,y^1);
addEdge(y^1,x^1);
}
}
}
memset(dfn,0,sizeof(dfn));
memset(vis,false,sizeof(vis));
memset(num,0,sizeof(num));
time_block=0;
scc=top=0;
for(int i=0;i<(m<<1);i++)
if(!dfn[i])
Tarjan(i);
bool flag=true;
for(int i=0;i<(m<<1);i+=2)
if(belong[i]==belong[i^1])//判断是否在同一个块中
flag=false;
if(flag)
printf("YES\n");
else
printf("NO\n");
return 0;
}