Katu Puzzle
Time Limit: 1000MS
Memory Limit: 65536K
Total Submissions: 11770
Accepted: 4345
Description
Katu Puzzle is presented as a directed graph G(V, E) with each edge e(a, b) labeled by a boolean operator op (one of AND, OR, XOR) and an integer c (0 ≤ c ≤ 1). One Katu is solvable if one can find each vertex Vi a value Xi (0 ≤ Xi ≤ 1) such that for each edge e(a, b) labeled by op and c, the following formula holds:
Xa op Xb = c
The calculating rules are:
AND
0
1
0
0
0
1
0
1
OR
0
1
0
0
1
1
1
1
XOR
0
1
0
0
1
1
1
0
Given a Katu Puzzle, your task is to determine whether it is solvable.
Input
The first line contains two integers N (1 ≤ N ≤ 1000) and M,(0 ≤ M ≤ 1,000,000) indicating the number of vertices and edges.
The following M lines contain three integers a (0 ≤ a < N), b(0 ≤ b < N), c and an operator op each, describing the edges.
Output
Output a line containing "YES" or "NO".
Sample Input
4 4
0 1 1 AND
1 2 1 OR
3 2 0 AND
3 0 0 XOR
Sample Output
YES
Hint
_X_0 = 1, _X_1 = 1, _X_2 = 0, _X_3 = 1.
Source
POJ Founder Monthly Contest – 2008.07.27, Dagger
题解
POJ3678
上面本来是有一个表格的,可能是复制下来的时候炸格式了,没事,博主再复述一遍题意
有N个变量(x_0-x_{n-1}),每个变量的可能取值是0或1.给定M个算式,每个算式形如(x_a) (op) (x_b) (=) (c),其中a,b是两个变量的编号,c是数字0或1,op是ans,or,xor三个位运算之一.求是否存在对每个变量的合法取值,是所有算式都成立
数据范围:(1<=N<=1000,1<=M<=10^6) 我们发现,如果只有xor运算,很显然我们是可以用扩展域并查集做的,但是现在有三种运算,又发现每种运算仅涉及两个变量,那么考虑2-SAT
在这里再简略的重复一下2-SAT的含义
现有一个由N个布尔值组成的序列A,给出一些限制关系,比如A[x] AND A[y]=0、A[x] OR A[y] OR A[z]=1等,要确定A[0..N-1]的值,使得其满足所有限制关系。这个称为SAT问题,特别的,若每种限制关系中最多只对两个元素进行限制,则称为2-SAT问题。
2-SAT就是把变量之间的关系转化为图形去解决
我们设对于一个变量i为表示这个变量的值为0的域,i+n为表示这个变量为1的域
(u,v)表示u,v之间有一条有向边,表示根据u必然能推出v
and
a and b=1
要求a和b都为1,那么如果题目a为0,则必须要让a=1,a->a+n
同理,如果b为0,b->b+n
a and b=0
要求a和b至少要有一个为0,那么如果a=1,则b必然为0,a+n->b
同理,b=1,b+n->a
or
a or b=1
要求a和b至少有一个为1,如果a=0,则必然b=1,a->b+n
同理,b=0,b->a+n
a or b=0
要求a和b都要为0,如果其中有一个为1,把它连到自己为0的域
a+n->a,b+n->b
xor
a xor b=1
要求a和b不同,即a=1,b=0,a=0,b=1
同时,b=1,a=0,b=0,a=1
a+n->b,a->b+n
b+n->a,b->a+n
a xor b=0
要求a和b相同,类比上面
a->b,b->a
a+n->b+n,b+n->a+n
建完图后跑tarjan缩点就可以了
判断:判断一个点为0的域和为1的域是否在同一个scc里面,如果在,就表示不合法,否则合法
Code
#include<iostream>
#include<cstdio>
#include<stack>
#include<cstring>
#include<string>
#include<cstdlib>
#define Max(a,b) (a)>(b)?(a):(b)
#define Min(a,b) (a)<(b)?(a):(b)
using namespace std;
const int N=2010;
inline void in(int &ans)
{
ans=0;int f=1;char i=getchar();
while(i<'0' || i>'9') {if(i=='-') f=-1;i=getchar();}
while(i>='0' && i<='9') {ans=(ans<<1)+(ans<<3)+i-'0';i=getchar();}
ans*=f;
}
stack<int>s;
int n,m,cnt,dfscnt,scc;
int nex[N*N],to[N*N],head[N*N];
int sccno[N],dfn[N],low[N],num[N];
void add(int a,int b)
{
to[++cnt]=b;
nex[cnt]=head[a];
head[a]=cnt;
}
void tarjan(int u)
{
dfn[u]=low[u]=++dfscnt; s.push(u);
for(int i=head[u];i;i=nex[i])
{
if(!dfn[to[i]])
{
tarjan(to[i]);
low[u]=Min(low[u],low[to[i]]);
}
else if(!sccno[to[i]]) low[u]=Min(low[u],dfn[to[i]]);
}
if(low[u]==dfn[u])
{
scc++;
while(1)
{
int x=s.top(); s.pop();
num[scc]++;
sccno[x]=scc;
if(x==u) break;
}
}
}
bool two_sat()
{
for(int i=0;i<=(n-1<<1);i++) if(!dfn[i]) tarjan(i);
for(int i=0;i<n;i++)
if(sccno[i]==sccno[i+n]) return 0;
return 1;
}
int main()
{
in(n),in(m);
for(int i=1;i<=m;i++)
{
int a,b,vl;char s[5];
in(a),in(b),in(vl);scanf("%s",s);
if(s[0]=='A')
{
if(vl) add(a,a+n),add(b,b+n);
else add(a+n,b),add(b+n,a);
}
else if(s[0]=='O')
{
if(vl) add(a,b+n),add(b,a+n);
else add(a+n,a),add(b+n,b);
}
else
{
if(vl) add(a,b+n),add(b,a+n),add(a+n,b),add(b+n,a);
else add(a,b),add(b,a),add(a+n,b+n),add(b+n,a+n);
}
}
if(two_sat()) puts("YES");
else puts("NO");
return 0;
}