2-SAT 模板
大致理解了。
需要注意的在这几个地方:
-
有确定关系的才能建边,比如说:若\(x\)为\(false\)时\(y\)一定为\(true\),则可以\(add(x,y+n)\),若\(x\)为\(false\)时\(y\)可能为\(true\)也可能为\(false\),则不能建边。
-
如果从\((x,false)\)能走到\((x,true)\),说明选择\((x,false)\)一定会产生矛盾,选择\((x,true)\)就不会,反之同理。若既能从\((x,false)\)走到\((x,true)\),又能\((x,true)\)能走到\((x,false)\)(在一个强联通分量中),那么就是无解。
-
每次选择时尽量选拓扑序大的那个,因为从拓扑序小的往后推则可能会与原来的选择产生矛盾,选择较大的那个就一定不会。
P4782 代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=3000000;
int n,m,cnt,tot,z,top;
int head[maxn],low[maxn],dfn[maxn],bel[maxn],s[maxn];
bool vis[maxn];
struct ED{
int to,next;
}e[maxn];
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<1)+(s<<3)+(ch^48);
ch=getchar();
}
return s*w;
}
inline void add(int u,int v){
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
return;
}
inline void tarjan(int u){
dfn[u]=low[u]=++tot;
s[++top]=u;
vis[u]=1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(vis[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u]){
int y;
z++;
do{
y=s[top--];
bel[y]=z;
vis[y]=0;
}while(y!=u);
}
}
int main(){
n=read();m=read();
for(int i=1;i<=m;++i){
int p=read(),a=read(),q=read(),b=read();
if(a&&b){
add(p,q+n);
add(q,p+n);
}else if(!a&&!b){
add(p+n,q);
add(q+n,p);
}else if(a&&!b){
add(p,q);
add(q+n,p+n);
}else if(!a&&b){
add(p+n,q+n);
add(q,p);
}
}
for(int i=1;i<=2*n;++i){
if(!dfn[i]) tarjan(i);
}
for(int i=1;i<=n;++i){
if(bel[i]==bel[i+n])
return printf("IMPOSSIBLE"),0;
}
printf("POSSIBLE\n");
for(int i=1;i<=n;++i){
if(bel[i]>bel[i+n]) printf("1 ");
else printf("0 ");
}
return 0;
}