题意:有n个布尔变量(x_1)~(x_n),另有m个需要满足的条件,每个条件的形式都是“(x_i)为(true/false)或(x_j)为(true/false)”.比如“(x_1)为真或(x_3)为假”、“(x_7)为假或(x_2)为假”.(2-SAT) 问题的目标是给每个变量赋值使得所有条件得到满足.(n,m<=100000).
分析:(2-SAT)一般注意两个点:建图和输出方案.
对于建图,一定是要满足"若p,则必须q"时才能连边.举个例子.如果(x_i=1)或者(x_j=1),则若(x_i=0),则(x_j)必须(=1),所以建有向边(i->j+n),同理若(x_j=0),则(x_i)必须(=1),所以建有向边(j->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=2e6+5;
int tot,head[N],nxt[N],to[N],opp[N],val[N];
int tim,top,num,dfn[N],low[N],st[N],color[N],belong[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(low[u]==dfn[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(),d=read();
if(!b&&!d)add(a+n,c),add(c+n,a);
if(!b&&d)add(a+n,c+n),add(c,a);
if(b&&!d)add(a,c),add(c+n,a+n);
if(b&&d)add(a,c+n),add(c,a+n);
}
for(int i=1;i<=2*n;++i)if(!dfn[i])tarjan(i);
for(int i=1;i<=n;++i){
if(color[i]==color[i+n]){
puts("IMPOSSIBLE");return 0;
}
opp[i]=n+i;opp[n+i]=i;
}
puts("POSSIBLE");
for(int i=1;i<=2*n;++i)
val[i]=color[i]>color[opp[i]];
for(int i=1;i<=n;++i){
if(!val[i])printf("0 ");
else printf("1 ");
}
printf("
");
return 0;
}