Description
给出一个关系,包括 And,Xor,Or 问是否存在解.
Sol
经典的2-SAT问题.
把每个值看成两个点,一个点代表选 (0) ,另一个代表选 (1) .
首先来看 Xor :
如果两个值异或起来为 (1) :那么连边 ((i_0,j_1),(i_1,j_0),(j_0,i_1),(j_1,i_0)) .
否则 连边 ((i_0,j_0),(i_1,j_1),(j_0,i_0),(j_1,i_1)) .
然后是 And.
如果两个值 And 起来为 (1) :连边 ((i_0,i_1),(j_0,j_1))
否则 连边 ((i_1,j_0),(j_1,i_0))
最后是 Or.
如果两个值 Or 起来为 (1) :连边 ((i_0,j_1),(j_1,i_0))
否则 连边 ((i_1,i_0),(j_1,j_0)) .
Tarjan 缩一下环判断一下两个点是否在一个环中就可以了.
Code
#include <cstdio> #include <iostream> #include <vector> using namespace std; const int N = 2005; int n,m,cnt,cntb; vector< int > g[N]; int d[N],b[N],ins[N]; int stk[N],top; inline int in(int x=0,char ch=getchar()){ while(ch>'9' || ch<'0') ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();return x; } void Add_Edge(int fr,int to) { g[fr].push_back(to); } void Tarjan(int u,int fa) { int dfsn=++cnt;d[u]=cnt,ins[u]=1,stk[++top]=u; for(int i=0,v;i<(int)g[u].size();i++) { v=g[u][i]; if(!d[v]) Tarjan(v,u); if(ins[v]) d[u]=min(d[u],d[v]); }if(dfsn==d[u]) { for(++cntb;stk[top]!=u;top--) { b[stk[top]]=cntb,ins[stk[top]]=0; }ins[stk[top]]=0,b[stk[top--]]=cntb; } // cout<<u<<":"<<dfsn<<" "<<d[u]<<endl; } int main() { // freopen("in.in","r",stdin); n=in(),m=in(); char opt[50]; for(int i=1;i<=m;i++) { int u=in(),v=in(),w=in(); scanf("%s",opt); if(u>v) swap(u,v); if(opt[0]=='X') { if(w) { Add_Edge(u*2,v*2+1); Add_Edge(u*2+1,v*2); Add_Edge(v*2,u*2+1); Add_Edge(v*2+1,u*2); }else { Add_Edge(u*2,v*2); Add_Edge(v*2,u*2); Add_Edge(u*2+1,v*2+1); Add_Edge(v*2+1,u*2+1); } }else if(opt[0]=='A') { if(w) { Add_Edge(u*2,u*2+1); Add_Edge(v*2,v*2+1); }else { Add_Edge(u*2+1,v*2); Add_Edge(v*2+1,u*2); } }else { if(w) { Add_Edge(u*2,v*2+1); Add_Edge(v*2,u*2+1); }else { Add_Edge(u*2+1,u*2); Add_Edge(v*2+1,v*2); } } } for(int i=0;i<2*n;i++) if(!d[i]) Tarjan(i,i); // for(int i=0;i<2*n;i++) cout<<b[i]<<" ";cout<<endl; for(int i=0;i<2*n;i++) if(b[i]==b[i^1]) return puts("NO"),0; return puts("YES"),0; }