【学习笔记】 2-SAT问题
Description
\(2-SAT\)问题就是给定一串变量,每个变量只能为真或假。
要求对这些变量进行赋值,满足方程所代表的限制条件,\(\rm 2-sat\) 问题中的限制是只与两个变量有关的,形如 $x_1 || x_2 \(,\)x_1 && (!x_2)$
如果这个条件变成三或者三以上个\(x\)相关的了,就只能\(2^n\)枚举了,即 \(N-SAT\) 问题是 \(NP\) 完全
Solution
首先将每个点拆成两个表示其为真或者为假,这里使用 \(i\) 表示这个点真点,\(i'\) 表示假点
问题中的图上的每条边 \((u,v)\) 表示如果 \(u\) 所代表的情况发生了,\(v\) 所代表的情况也要发生,举例而言即形如 \(x || y\) 的限制 则连 \((x',y),(y',x)\) 即如果 \(x\) 假了则 \(y\) 必须真
而题目中的限制也常可以表示成或而不是与的形式,建图时另外一个需要注意的点就是连接表示逆否命题的边
之后 \(tarjan\) 求强连通分量,如果有 scc[i]==scc[i+n]
则无解,原因是同一个强连通分量里面的点表示一个情况的并集,一个点的真假性是唯一的
对于求解 bool
方程每个变量的解,结论是 x_i=[scc[i]<scc[i+n]]
如果 \(x_i\) 可以到达 \(x'_i\) 但是反向不行,那么只能选择 \(x'_i\),也就是其所在的强连通分量在拓扑序上排名较后的一个
这时候可以踩在巨人 \(\rm tarjan\) 的肩膀上:使用 \(\rm scc\) 序是拓扑序的逆序这一结论得到答案
例题
NEERC2016 Binary Code
前后缀优化建图+ \(2-sat\) 模板题,在 这篇博客 中已经有写到
JSOI2019 精准预测
(其实写这篇博客就是为了写这题题解)
将每个火星人在每个时间存在与否建立 \(2\times n(T+1)\) 个点,根据实际含义,每个人本身在 \(i\) 时刻存活则在 \(i-1\) 时刻存活,在 \(i\) 时刻死亡则在 \(i+1\) 时刻死亡
再根据实际含义,对于操作一,如果 \(y\) 在 \(i+1\) 存活则 \(x\) 在 \(i\) 存活,也要连逆否命题
对于操作二,\(y\) 在 \(i\) 时刻存活则对应 \(x\) 在 \(i\) 时刻死亡,也不要忘记连逆否命题
乍一看这点数很合理,已经降到 \(4m+2n\) 了,但是还能再少:真的有必要在 \(i/i+1\) 时刻给 \(y\) 建两个点吗?
其实并不需要,站到统计答案的角度看,我们只关注在 \(T+1\) 时刻的生死,具体是什么时候死的不重要
只存每个点作为 \(x\) 时刻的生死点和 \(T+1\) 时刻的生死点,作为 \(y\) 时可以 lower_bound
下一个作为 \(x\) 的时候
最后是统计答案,不能同生的是自己生的后继中它人死,和那些自己生的后继中有自己死的点,注意去重
这明显是一个 \(DAG\) 上可达点统计问题,使用 bitset
优化可以做到 \(\Theta(\frac {nm}{\omega})\)