把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P4782 【模板】2-SAT 问题

题面传送门
我们考虑拆点,将一个点拆成\(1\)\(0\)两个点。
考虑怎么建图,\(a=na\)\(b=nb\)成立就是如果\(b=nb\bigoplus1\)那么\(a=na\),连一条\(b\)\(nb\bigoplus1\)\(a\)\(na\)的边表示一旦选\(nb\bigoplus1\)就一定是\(na\)
然后如果两点在一个强连通分量中那么就可以直接无解了。
输出方案我们考虑如果\(0\)可以到\(1\),那么一定是选\(1\),因为选了\(0\)就一定是\(1\)就矛盾了。
这样子我们用tarjan的逆拓扑序判断即可。
code:

#include <vector>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#include<bitset>
#include<set>
#include<map>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 2000000
#define mod 1000000007
#define eps (1e-7)
#define U unsigned int
#define IT set<ques>::iterator
#define Gc() getchar() 
I void read(int &x){
	char s=getchar();x=0;while(s<'0'||s>'9') s=Gc(); 
	while(s>='0'&&s<='9') x=x*10+s-48,s=Gc();
}
using namespace std;
int n,m,dfn[N+5],dh,scc[N+5],low[N+5],st[N+5],sh,vis[N+5],x,y,nx,ny,cnt;
struct yyy{int to,z;};
struct ljb{
	int head,h[N+5];yyy f[N+5];
	I void add(int x,int y){f[++head]=(yyy){y,h[x]};h[x]=head;/*printf("%d %d\n",x,y);*/}
}s;
I void tarjan(int x){
	vis[x]=1;dfn[x]=low[x]=++dh;st[++sh]=x;yyy tmp;for(int i=s.h[x];i;i=tmp.z){
		tmp=s.f[i];if(!dfn[tmp.to]) tarjan(tmp.to),low[x]=min(low[x],low[tmp.to]);
		else vis[tmp.to]&&(low[x]=min(low[tmp.to],low[x]));
	}
	if(low[x]==dfn[x]){++cnt;while(st[sh+1]^x) scc[st[sh]]=cnt,vis[st[sh--]]=0;}
}
int main(){
//	freopen("1.in","r",stdin);
	re int i;scanf("%d%d",&n,&m);while(m--)read(x),read(nx),read(y),read(ny),s.add(x+n*(nx^1),y+n*ny),s.add(y+n*(ny^1),x+nx*n);
	for(i=1;i<=n*2;i++)!dfn[i]&&(tarjan(i),0);for(i=1;i<=n;i++) if(scc[i]==scc[i+n]){printf("IMPOSSIBLE\n");return 0;}printf("POSSIBLE\n");
	for(i=1;i<=n;i++)printf("%d ",scc[i]>scc[i+n]); 
}
posted @ 2021-06-24 18:50  275307894a  阅读(30)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end