2-sat 是一种逻辑关系,考虑如何在绝对成立情况之间建边
形成 DAG 然后用 trajan 缩点,判断是否存在 a = 1 和 a = 0 的情况同时存在一个连通分量里,存在则矛盾,不存在则成立
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#define MAXN 2005
using namespace std;
struct Edge{
int v,next;
}edge[MAXN*MAXN];
int cnt,head[MAXN];
int n,m,block_cnt,scc_cnt;
int dfn[MAXN],low[MAXN],scc[MAXN];
bool vis[MAXN];
stack<int> s;
void add(int u,int v)
{
edge[++cnt].next = head[u];
edge[cnt].v = v;
head[u]=cnt;
}
void init()
{
memset(head,0,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(scc,0,sizeof(scc));
memset(vis,false,sizeof(vis));
cnt=block_cnt=scc_cnt=0;
}
void get()
{
for(int i=1;i<=m;i++){
char op[5];
int a,b,val;
scanf("%d%d%d%s",&a,&b,&val,op);
if(op[0]=='A'){ //AND操作
if(val==0){ //a and b=0
add(a,b+n); //a为1,b为0
add(b,a+n); //b为1,a为0
}
else{ //a and b=1
add(a+n,a); //a必为1
add(b+n,b); //b必为1
}
}
else if(op[0]=='O'){ //OR操作
if(val==0){ //a or b=0
add(a,a+n); //a必为0
add(b,b+n); //b必为0
}
else{ //a or b=1
add(a+n,b); //a为0,b为1
add(b+n,a); //b为0,a为1
}
}
else{ //XOR操作
if(val==0){ //命a xor b=0
add(a+n,b+n); //a为0,b为0
add(b+n,a+n); //b为0,a为0
add(a,b); //a为1,b为1
add(b,a); //b为1,a为1
}
else{ //a xor b=1
add(a,b+n); //a为1,b为0
add(b,a+n); //b为1,a为0
add(a+n,b); //a为0,b为1
add(b+n,a); //b为0,a为1
}
}
}
}
void tarjan(int u)
{
dfn[u]=low[u]=++block_cnt; //时间戳
vis[u]=true; //标记入栈
s.push(u);
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].v;
if(!dfn[v]){ //还没访问过
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v]) //已访问过且还在栈中
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){ //满足强连通分量
scc_cnt++;
while(1){
int tmp=s.top();s.pop();
scc[tmp]=scc_cnt; //标记所属强连通分量
vis[tmp]=false; //标记出栈
if(tmp==u)break; // 找到这个强连通分量的根节点,代表已经把这个强连通分量的成员全部加入,此时可以退出进入下一个强连通分量
}
}
}
bool TwoSat()
{
for(int i=0;i<2*n;i++) // 设 a = 1, a+n = 0; 所以共 2*n 个点
if(!dfn[i])
tarjan(i); // 利用 tarjan 缩点
for(int i=0;i<n;i++)
if(scc[i]==scc[i+n]) //矛盾
return false;
return true;
}
int main()
{
while(~scanf("%d%d",&n,&m)){
init(); //初始化
get(); //读入数据
if(TwoSat()) printf("YES\n");
else printf("NO\n");
}
}