题意:
n个权值为0或1的数。
给定c个限制条件,形如:
XA
xor
XB=1(0)
XA
or
XB=1(0)
XA
and
XB=1(0)
判定是否可以安排这n个数的值,满足所有限制条件。
解析:
据说是2-SAT问题,反正我刚看到的时候满脑子都是建出来一个图之后判环。。
然而是有科学的做法的。
首先我们把每个点拆分成两个,分别代表改点取0或1.
科学的做法就是按照矛盾关系建边。
打一个比方
如果
XA
xor
XB=1
的话,
当
XA
取1的时候,
XB
一定取0,所以连一条边。
当
XA
取0的时候,
XB
一定取1,所以连一条边。
当
XB
取1的时候,
XA
一定取0,所以连一条边。
当
XB
取0的时候,
XA
一定取1,所以连一条边。
每一种限制条件都像这样讨论就好了。
但是需要注意的是,有的时候
XA
取0(1)一定无解。
所以我们要相应的从该点连向
XA
取1(0)的边,代表如果
XA
取0(1)一定无解。
最后我们只需要tarjan缩下点观察每个点取0或者1的两个点是否在一个强连通分量里即可。
如果有一个在的话,那么显然无解。
否则有解。
如果这道题要输出方案的话,显然有解的情况最后的图是一个DAG,拓扑染色一下即可。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 2100
using namespace std;
int head[N];
int cnt,n,m;
struct node
{
int from,to,next;
}edge[N*N];
void init()
{
memset(head,-1,sizeof(head));
cnt=1;
}
void edgeadd(int from,int to)
{
edge[cnt].from=from,edge[cnt].to=to;
edge[cnt].next=head[from];
head[from]=cnt++;
}
char s[5];
int cnt_block,tot,top;
int deep[N],low[N],sta[N],belong[N],ins[N];
void tarjan(int now)
{
deep[now]=low[now]=++tot;
sta[++top]=now,ins[now]=1;
for(int i=head[now];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(!deep[to])
{
tarjan(to);
low[now]=min(low[now],low[to]);
}else if(ins[to])low[now]=min(low[now],deep[to]);
}
if(deep[now]==low[now])
{
cnt_block++;
int t=-1;
do
{
t=sta[top--];
belong[t]=cnt_block;
ins[t]=0;
}while(t!=now);
}
}
int main()
{
init();
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y,val;
scanf("%d%d%d%s",&x,&y,&val,s);
x++,y++;
if(s[0]=='X')
{
if(val==1)
edgeadd(x+n,y),edgeadd(y,x+n),edgeadd(x,y+n),edgeadd(y+n,x);
else
edgeadd(x+n,y+n),edgeadd(y+n,x+n),edgeadd(x,y),edgeadd(y,x);
}else if(s[0]=='A')
{
if(val==1)
edgeadd(x,y),edgeadd(y,x),edgeadd(x+n,x),edgeadd(y+n,y);
else edgeadd(x,y+n),edgeadd(y,x+n);
}else
{
if(val==1)
edgeadd(x+n,y),edgeadd(y+n,x);
else edgeadd(x,x+n),edgeadd(y,y+n),edgeadd(x+n,y+n),edgeadd(y+n,x+n);
}
}
for(int i=1;i<=2*n;i++)
if(!deep[i])tarjan(i);
int flag=1;
for(int i=1;i<=n;i++)
if(belong[i]==belong[i+n]){flag=0;break;}
if(flag)puts("YES");
else puts("NO");
}