Katu Puzzle

POJ

题意:有(N)个变量(x_1-x_n),每个变量的可能取值是0或1.给定(M)个算式,每个算式形如(X_a) (op) (X_b=c),其中(a,b)是两个变量的编号,(c)是数字0或1,(op)(and),(or),(xor)三个位运算之一.求是否存在对每个变量的合法赋值,使得所有算式都成立.(n<=1000,m<=10^6.)

分析:(2-SAT)问题,先要转换成(2-SAT)的形式.设节点(a)表示(x_a)赋值为0,节点(a+n)表示(x_a)赋值为1:

(a) (and) (b=0),则(x_a=1)时,(x_b)必须(=0),连有向边((a+n,b)) .其它情况都这样去想就好.注意:能够连边要满足的条件是:"若p,则必须q".

建图后,跑有向图(Tarjan),如果有一个节点(i),(i)(i+n)都属于同一个强连通分量,则不存在合法赋值了.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=1005;
const int M=4e6+5;
int tot,head[N],nxt[M],to[M];
int tim,top,num,dfn[N],low[N],st[M],color[N];
inline void add(int a,int b){nxt[++tot]=head[a];head[a]=tot;to[tot]=b;}
inline void tarjan(int u){
	dfn[u]=low[u]=++tim;st[++top]=u;
	for(int i=head[u];i;i=nxt[i]){
		int v=to[i];
		if(!dfn[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(!color[v])low[u]=min(low[u],dfn[v]);
	}
	if(dfn[u]==low[u]){
		color[u]=++num;
		while(st[top]!=u){
			color[st[top]]=num;
			--top;
		}
		--top;
	}
}
int main(){
	int n=read(),m=read();
	for(int i=1;i<=m;++i){
		int a=read(),b=read(),c=read();
		++a;++b;string s;cin>>s;
		if(s[0]=='A'){
			if(c==0)add(a+n,b),add(b+n,a);
			else add(a,a+n),add(b,b+n);
		}
		else if(s[0]=='O'){
			if(c==0)add(a+n,a),add(b+n,b);
			else add(a,b+n),add(b,a+n);
		}
		else if(s[0]=='X'){
			if(c==0){
				add(a,b);add(b,a);
				add(a+n,b+n);add(b+n,a+n);
			}
			else{
				add(a,b+n);add(b,a+n);
				add(a+n,b);add(b+n,a);
			}
		}
	}
	for(int i=1;i<=n*2;++i)if(!dfn[i])tarjan(i);
	for(int i=1;i<=n;++i)
		if(color[i]==color[i+n]){
			puts("NO");return 0;
		}
	puts("YES");
    return 0;
}

原文地址:https://www.cnblogs.com/PPXppx/p/11592627.html