建边方略:
AND = 1 : ~x -> x ,~y -> y (两个数必须全为1)
AND = 0 : y -> ~x ,x -> ~y (两个数至少有一个为0)
OR = 1 : ~x -> y ,~y -> x (两个数至少有一个为1)
OR = 0 : x -> ~x ,y -> ~y (两个数全为0)
XOR = 1 : x -> ~y ,y -> ~x ,~y -> x ,~x -> y(两个数不同)
XOR = 0 : x -> y ,~x -> ~y ,y -> x ,~y -> ~x(两个数相同)
x,y不能都选是(基础的矛盾) : x -> ~y ,y -> ~x
x,y不能都选否 :~x -> y ,~y -> x
不能同时x选是,y选否 :x -> y ,~y -> ~x
不能同时x选否,y选是 :~x -> ~y ,y -> x
1月份做的题,现在补可还行。 这个题并查集也可以搞,当时就是并查集过的。
#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn=4e5+5;
vector<int> e[maxn],loc[maxn];
int n,m;
int a[maxn];
int dfn[maxn],low[maxn],in[maxn],ind=0;
stack<int> q;
void targan(int x){
low[x]=dfn[x]=++ind;
q.push(x);
in[x]=1;
for(int i=0;i<e[x].size();i++){
int y=e[x][i];
if(!dfn[y]){
targan(y);
low[x]=min(low[x],low[y]);
}else if(low[x]>dfn[y]&&in[y]){
low[x]=dfn[y];
}
}
if(low[x]==dfn[x]){
while(q.top()!=x){
in[q.top()]=0;
q.pop();
}
q.pop();
in[x]=0;
}
}
void slove(){
for(int i=1;i<=2*m;i++){
if(!dfn[i]) targan(i);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]=a[i]^1;
int k,tp;
for(int i=1;i<=m;i++){
scanf("%d",&k);
for(int j=1;j<=k;j++){
scanf("%d",&tp);
loc[tp].push_back(i);
}
}
int flag=true;
for(int i=1;i<=n;i++){
int size=loc[i].size(),p,q;
if(0==size){
if(!a[i]){
flag=false;
break;
}
}else if(1==size){
p=loc[i][0];
if(a[i]){
e[p].push_back(p+m);
}else{
e[p+m].push_back(p);
}
}else if(2==size){
p=loc[i][0],q=loc[i][1];
if(a[i]){
e[p].push_back(q);
e[q].push_back(p);
e[p+m].push_back(q+m);
e[q+m].push_back(p+m);
}else{
e[p].push_back(q+m);
e[q].push_back(p+m);
e[p+m].push_back(q);
e[q+m].push_back(p);
}
}
}
if(flag) slove();
for(int i=1;i<=m;i++){
if(low[i]==low[i+m]){
flag=false;
break;
}
}
if(flag) printf("YES\n");
else printf("NO\n");
return 0;
}