【学习笔记】2-SAT
2-SAT 用来解决一类如下形式问题:有 \(n\) 个针对布尔变量的或条件,求一组解。
考虑按照命题连边,也就是从中找到必要的逻辑关系进行建图。那么显然,有两条边:一条是原命题,一条是逆否命题。
建图之后跑缩点,那么缩在一起的点一定是可以互相推出的。
那么,如果 \(x\) 与 \(!x\) 合并到了一起,那么它就是无解的。否则,考虑按照拓扑序逆序构造解,看 \(x\) 对应的变量哪个在后,选择在后的那个为 \(1\) 即可。
考虑证明在 2-SAT 问题对称性下算法的正确性。
考虑按照拓扑序从大到小选择点,那么每次我选择这个点,然后把它对应的非变量染色为不选。那么每次我染为不选的都一定是一条前缀链。
那么这样,在保证 有解 的情况下,一定不会有两个互逆变量同时被选择,现在只需要证明不会有两个变量被同时不选即可。
有如下情况:
- 假设 \(y\) 的拓扑序大,它被选择,并且连了两条不同的边指向 \(x,!x.\)
那么把图建立出来会发现,\(!y\) 的拓扑序是比 \(y\) 大的。条件不成立。
- 假设 选择了 \(y,z\) 并且它们分别指向 \(x,!x.\)
建立出逻辑图之后,发现 \(!z,!y\) 的拓扑序大于 \(y,z,\) 题设不成立。
- 假设选择了 \(y\) 并且它染色为不选的链上挂着 \(x,!x.\)
考虑建立逻辑图之后,发现会形成环。上述链当形如:\((y)\to (!y)\to (a)\to (x)\to (!a)\to (!x)\)
这里 \((!a)\to (y)\) 是存在的边,所以无解,与题设不成立。
综上,可以得出 2-SAT 算法的正确性。
#include <bits/stdc++.h>
using namespace std;
typedef double db;
//#define int long long
#define fi first
#define se second
#define mk make_pair
#define pb emplace_back
#define poly vector<int>
#define Bt(a) bitset<a>
#define bc __builtin_popcount
#define pc putchar
#define ci const int&
const int mod = 1e9 + 7;
const db eps = 1e-10;
inline int Max(ci x, ci y) {return x > y ? x : y;}
inline int Min(ci x, ci y) {return x < y ? x : y;}
inline db Max(db x, db y) {return x - y > eps ? x : y;}
inline db Min(db x, db y) {return x - y < eps ? x : y;}
inline int Add(ci x, ci y, ci M = mod) {return (x + y) % M;}
inline int Mul(ci x, ci y, ci M = mod) {return 1ll * x * y % M;}
inline int Dec(ci x, ci y, ci M = mod) {return (x - y + M) % M;}
typedef pair<int, int> pii;
inline int Abs(int x) {return x < 0 ? -x : x;}
//char buf[1<<21],*p1=buf,*p2=buf;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char Obuf[105000],*O=Obuf;//Siz shoule be the size of Out File
int pst[30],ptop;
inline void Fprint(){fwrite(Obuf,1,O-Obuf,stdout);}
inline void Fwrite(int x){
if(x==0){*O++='0';return;}
if(x<0)*O++='-',x=-x;ptop=0;
while(x)pst[++ptop]=x%10,x/=10;
while(ptop)*O++=pst[ptop--]+'0';
if(O-Obuf>100000)Fprint(),O=Obuf;
}
inline int read() {
int s = 0, w = 1;
char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') w = -1;ch = getchar();}
while (isdigit(ch)) {s = s * 10 + ch - '0';ch = getchar();}
return s * w;
}
inline void write(int x) {
if (x < 0)putchar('-'), x = -x;
if (x > 9)write(x / 10);
pc(x % 10 + '0');
}
inline int qpow(int x, int y) {
int res = 1;
while (y) {if (y & 1)res = Mul(res, x);x = Mul(x, x);y >>= 1;}
return res;
}
inline void cadd(int &x, int y) {x += y;}
inline void cmul(int &x, int y) {x *= y;}
inline void cmax(int &x, int y) {x = Max(x, y);}
inline void cmin(int &x, int y) {x = Min(x, y);}
const int N = 2e6 + 10;
namespace Refined_heart{
poly G[N];
inline void link(int x,int y){G[x].pb(y);}
int dfstime,top,st[N],dfn[N],low[N],ccf;
int n,m,col[N],inst[N];
void tarjan(int x){
dfn[x]=low[x]=++dfstime;
st[++top]=x;inst[x]=1;
for(auto v:G[x]){
if(!dfn[v]){
tarjan(v);
cmin(low[x],low[v]);
}
else if(inst[v])cmin(low[x],dfn[v]);
}
if(low[x]==dfn[x]){
int y=0;++ccf;
while(y=st[top--]){
col[y]=ccf;
inst[y]=0;
if(x==y)break;
}
}
}
void solve(){
n=read();m=read();
for(int i=1;i<=m;++i){
int a=read(),va=read(),b=read(),vb=read();
if(va&&vb)link(a+n,b),link(b+n,a);
if(!va&&!vb)link(a,b+n),link(b,a+n);
if(!va&&vb)link(a,b),link(b+n,a+n);
if(va&&!vb)link(a+n,b+n),link(b,a);
}
for(int i=1;i<=n+n;++i)if(!dfn[i])tarjan(i);
for(int i=1;i<=n;++i){
if(col[i]==col[i+n]){
puts("IMPOSSIBLE");
exit(0);
}
}
puts("POSSIBLE");
for(int i=1;i<=n;++i){
if(col[i]<col[i+n])pc('1');
else pc('0');
pc(' ');
}
}
}
int main(){
Refined_heart::solve();
return 0;
}