有n对夫妻被邀请参加一个聚会,因为场地的问题,每对夫妻中只有1人可以列席。在2n 个人中,某些人之间有着很大的矛盾(当然夫妻之间是没有矛盾的),有矛盾的2个人是不会同时出现在聚会上的。有没有可能会有n 个人同时列席?
Input
n: 表示有n对夫妻被邀请 (n<= 1000)
m: 表示有m 对矛盾关系 ( m < (n - 1) * (n -1))

在接下来的m行中,每行会有4个数字,分别是 A1,A2,C1,C2
A1,A2分别表示是夫妻的编号
C1,C2 表示是妻子还是丈夫 ,0表示妻子 ,1是丈夫
夫妻编号从 0 到 n -1
Output
如果存在一种情况 则输出YES
否则输出 NO
Sample Input
2
1
0 1 1 1
Sample Output
YES


很明显的2-SAT,直接先逻辑连边,然后tarjan即可。

注意注释的部分,一个人不去,不能推出另一个人去,所以我们不能加边!!!


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=10010,M=1e6+10;
int n,m,vis[N],low[N],dfn[N],cnt,col[N],co,base,flag;
int head[N],nex[M],to[M],tot;
stack<int> st;
inline void add(int a,int b){
	to[++tot]=b; nex[tot]=head[a]; head[a]=tot;
}
inline void init(){
	memset(head,0,sizeof head); tot=flag=co=cnt=0;
	memset(col,0,sizeof col);	memset(dfn,0,sizeof dfn);
	memset(vis,0,sizeof vis);	while(st.size())	st.pop();
}
void tarjan(int x){
	low[x]=dfn[x]=++cnt; vis[x]=1;	st.push(x);
	for(int i=head[x];i;i=nex[i]){
		if(!dfn[to[i]]){
			tarjan(to[i]);	low[x]=min(low[x],low[to[i]]);
		}else if(vis[to[i]]) low[x]=min(low[x],dfn[to[i]]);
	}
	if(low[x]==dfn[x]){
		co++;	int u;
		do{
			u=st.top();	st.pop();	col[u]=co;	vis[u]=0;
		}while(u!=x);
	}
}
signed main(){
	while(~scanf("%d %d",&n,&m)){
		init();	base=n<<1;
		for(int i=1;i<=n;i++)	
			add(i,i+n+base),add(i+n,i+base),add(i+base,i+n),add(i+n+base,i);
		while(m--){
			int a1,a2,c1,c2; scanf("%d %d %d %d",&a1,&a2,&c1,&c2);
			a1++; a2++;
			add(a1+(c1^1)*n,a2+(c2^1)*n+base);
			add(a2+(c2^1)*n,a1+(c1^1)*n+base);
		// add(a1+(c1^1)*n+base,a2+(c2^1)*n);
		// add(a2+(c2^1)*n+base,a1+(c1^1)*n);
		}
		for(int i=1;i<=base<<1;i++)	if(!dfn[i])	tarjan(i);
		for(int i=1;i<=base&&!flag;i++)	if(col[i]==col[i+base])	flag++;
		puts(flag?"NO":"YES");
	}
	return 0;
}