有N个变量X0~XN−1,每个变量的可能取值为0或1。
给定M个算式,每个算式形如 XaopXb=c,其中 a,b 是变量编号,c 是数字0或1,op 是 and,or,xor 三个位运算之一。
求是否存在对每个变量的合法赋值,使所有算式都成立。
输入格式
第一行包含两个整数N和M。
接下来M行,每行包含三个整数a b c,以及一个位运算(AND,OR,XOR中的一个)。
输出格式
输出结果,如果存在,输出“YES”,否则输出“NO”
数据范围
1≤N≤1000,
1≤M≤106
输入样例:
4 4
0 1 1 AND
1 2 1 OR
3 2 0 AND
3 0 0 XOR
输出样例:
YES
思路:
今天CCPC热身出了个2-SAT模板题,赶紧学一学。
将x拆成x与x+n,建边然后跑tarjan判断x与x+n是否在同一连通分量,在就代表x取0的时候得取1,取1的时候得取0,这明显矛盾,此时无解,其他情况可以构造出解。
x+n->y,代表着x取1,那么y必须取0,
x->y+n,代表着x取0,那么y必须取1。
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 7;
vector<int>G[maxn];
int dfn[maxn],low[maxn],stk[maxn],ins[maxn],top,cnt,num;
int scc[maxn];
void tarjan(int x) {
stk[++top] = x;
low[x] = dfn[x] = ++num;
ins[x] = 1;
for(int i = 0;i < G[x].size();i++) {
int v = G[x][i];
if(!dfn[v]) {
tarjan(v);
low[x] = min(low[x],low[v]);
} else if(ins[v]) {
low[x] = min(low[x],dfn[v]);
}
}
if(dfn[x] == low[x]) {
++cnt;
int y;
do {
y = stk[top--];
scc[y] = cnt;
ins[y] = 0;
} while(x != y);
}
}
void add(int x,int y) {
G[x].push_back(y);
}
int main() {
int n,m;scanf("%d%d",&n,&m);
for(int i = 1;i <= m;i++) {
int x,y,z;
char op[10];
scanf("%d%d%d%s",&x,&y,&z,op);
if(op[0] == 'A') {
if(z) {
add(x,x + n);
add(y,y + n);
} else {
add(x + n,y);
add(y + n,x);
}
} else if(op[0] == 'O') {
if(z) {
add(x,y + n);
add(y,x + n);
} else {
add(x + n,x);
add(y + n,y);
}
} else if(op[0] == 'X') {
if(z) {
add(x,y + n);
add(y,x + n);
add(x + n,y);
add(y + n,x);
} else {
add(x,y);
add(y,x);
add(x + n,y + n);
add(y + n,x + n);
}
}
}
for(int i = 0;i < n;i++) {
if(!dfn[i]) tarjan(i);
}
for(int i = 0;i < n;i++) {
if(scc[i] == scc[i + n]) {
printf("NO\n");
return 0;
}
}
printf("YES\n");
return 0;
}