// !在实际问题中,2-SAT问题在大多数时候表现成以下形式: // 有N对物品,每对物品中必须选取一个,也只能选取一个, // 并且它们之间存在某些`限制关系`,比如「A和B不能同时选取」、「C和D必须同时选取」。 // (如某两个物品不能都选,某两个物品不能都不选,某两个物品必须且只能选一个,某个物品必选)等, // 这时,可以将每对物品当成一个布尔值(选取第一个物品相当于0,选取第二个相当于1), // 如果所有的限制关系最多只对两个物品进行限制, // 则它们都可以转化成9种基本限制关系,从而转化为2-SAT模型。 // !非常像种类并查集 // https://www.cnblogs.com/kuangbin/archive/2012/10/05/2712429.html // 有n个布尔变量x1~xn,另有m个需要满足的条件,每个条件的形式都是`xi为true/false` 或 `xj为true/false`。 // 比如「x1为真或x3为假」、「x7为假或x2为假」。 // https://www.luogu.com.cn/problem/P4782 // https://ei1333.github.io/library/graph/others/two-satisfiability.hpp // 2-Satisfiability (2-SAT) // !https://zhuanlan.zhihu.com/p/50211772 // todo https://www.luogu.com.cn/blog/85514/post-2-sat-xue-xi-bi-ji // 2-SAT 总结 by kuangbin https://www.cnblogs.com/kuangbin/archive/2012/10/05/2712429.html // !NOTE: 一些建边的转换(命题为真对应0-n-1,命题为假对应n-2*n-1): // A 为真 (A) ¬A⇒A 注:A ⇔ A∨A ⇔ ¬A⇒A∧¬A⇒A ⇔ ¬A⇒A // A 为假 (¬A) A⇒¬A // A 为真 B 就为真 A⇒B, ¬B⇒¬A // A 为假 B 就为假 ¬A⇒¬B, B⇒A // !A,B 至少存在一个 (A|B) ¬A⇒B, ¬B⇒A 意思是一个为假的时候,另一个一定为真 https://www.luogu.com.cn/problem/P4782 // A,B 不能同时存在 (¬A|¬B) A⇒¬B, B⇒¬A 就是上面的式子替换了一下(一个为真,另一个一定为假) // A,B 必须且只一个 (A^B) A⇒¬B, B⇒¬A, ¬A⇒B, ¬B⇒A // A,B 同时或都不在 (¬(A^B)) A⇒B, B⇒A, ¬A⇒¬B, ¬B⇒¬A // !NOTE: 单独的条件 x为a 可以用 (x为a)∨(x为a) 来表示 // 模板题 https://www.luogu.com.cn/problem/P4782 // 建边练习【模板代码】 https://codeforces.com/contest/468/problem/B // 定义 Ai 表示「选 Xi」,这样若两个旗子 i j 满足 |Xi-Xj| 2SAT // n很大的时候一定有重复串,可以排除 // !n不大的时候用2SAT解决 :命题i代表S[i]用1个字符 func yuki470() { in := bufio.NewReader(os.Stdin) out := bufio.NewWriter(os.Stdout) defer out.Flush() var n int fmt.Fscan(in, &n) words := make([]string, n) for i := 0; i < n; i++ { fmt.Fscan(in, &words[i]) } // 只使用a-z和A-Z的字符,一个字符一定有重复的 if n > 26*2 { fmt.Fprintln(out, "Impossible") return } ts := NewTwoSat(n) // 枚举所有的对,看哪些s[i]不能同时用一个字符划分 // かぶさる可能性のあるものを反転させたものをグラフに追加する ?? for i := 0; i < n; i++ { w1 := words[i] for j := i + 1; j < n; j++ { w2 := words[j] // 1 1 s1, t1, s2, t2 := w1[0:1], w1[1:], w2[0:1], w2[1:] if s1 == s2 || t1 == t2 { ts.AddNand(i, j) } // 1 2 s1, t1, s2, t2 = w1[0:1], w1[1:], w2[0:2], w2[2:] if s1 == t2 || t1 == s2 { ts.AddNand(i, ts.Rev(j)) } // 2 1 s1, t1, s2, t2 = w1[0:2], w1[2:], w2[0:1], w2[1:] if s1 == t2 || t1 == s2 { ts.AddNand(ts.Rev(i), j) } // 2 2 s1, t1, s2, t2 = w1[0:2], w1[2:], w2[0:2], w2[2:] if s1 == s2 || t1 == t2 { ts.AddNand(ts.Rev(i), ts.Rev(j)) } } } res, ok := ts.Solve() if !ok { fmt.Fprintln(out, "Impossible") return } for i := 0; i < n; i++ { if res[i] { s, t := words[i][0:1], words[i][1:] fmt.Fprint(out, s, " ", t) } else { s, t := words[i][0:2], words[i][2:] fmt.Fprint(out, s, " ", t) } fmt.Fprintln(out) } } // No.1078 I love Matrix Construction // https://yukicoder.me/problems/no/1078 // 给定长为n的数组S,T,U // 问能否构建出满足以下条件的n*n矩阵A // 1. A[i][j] 为0/1 // 2. 对所有的 (i,j), A[S[i]][j]+A[j][T[i]]*2 != U[i] // n<=500 func yuki1078() { in := bufio.NewReader(os.Stdin) out := bufio.NewWriter(os.Stdout) defer out.Flush() var n int fmt.Fscan(in, &n) S := make([]int, n) for i := 0; i < n; i++ { fmt.Fscan(in, &S[i]) S[i]-- } T := make([]int, n) for i := 0; i < n; i++ { fmt.Fscan(in, &T[i]) T[i]-- } U := make([]int, n) for i := 0; i < n; i++ { fmt.Fscan(in, &U[i]) } // 条件i为A[i][j]取0 ts := NewTwoSat(n * n) for i := 0; i < n; i++ { si := S[i] ti := T[i] for j := 0; j < n; j++ { pos1 := si*n + j pos2 := j*n + ti if U[i] == 0 { ts.AddNand(pos1, pos2) // 0,0 } else if U[i] == 1 { ts.AddNand(ts.Rev(pos1), pos2) //1,0 } else if U[i] == 2 { ts.AddNand(pos1, ts.Rev(pos2)) //0,1 } else if U[i] == 3 { ts.AddNand(ts.Rev(pos1), ts.Rev(pos2)) //1,1 } } } res, ok := ts.Solve() if !ok { fmt.Fprintln(out, -1) return } matrix := make([][]int, n) for i := 0; i < n; i++ { matrix[i] = make([]int, n) } for i, v := range res { if !v { matrix[i/n][i%n] = 1 } } for i := 0; i < n; i++ { for j := 0; j < n; j++ { fmt.Fprint(out, matrix[i][j], " ") } fmt.Fprintln(out) } } // https://judge.yosupo.jp/problem/two_sat // N 変数 M 節の 2 Sat が与えられる。充足可能か判定し、可能ならば割り当てを一つ求めてください。 func yosupo() { in := bufio.NewReader(os.Stdin) out := bufio.NewWriter(os.Stdout) defer out.Flush() var p, cnf string var n, m int fmt.Fscan(in, &p, &cnf, &n, &m) ts := NewTwoSat(n) for i := 0; i < m; i++ { var a, b, c int fmt.Fscan(in, &a, &b, &c) if a < 0 { a++ a = ts.Rev(-a) } else { a-- } if b < 0 { b++ b = ts.Rev(-b) } else { b-- } ts.AddOr(a, b) } res, ok := ts.Solve() if !ok { fmt.Fprintln(out, "s UNSATISFIABLE") return } fmt.Fprintln(out, "s SATISFIABLE") fmt.Fprint(out, "v ") for i, v := range res { if v { fmt.Fprint(out, i+1, " ") } else { fmt.Fprint(out, -(i + 1), " ") } } fmt.Fprintln(out, 0) } type TwoSat struct { size int graph [][]int32 } func NewTwoSat(n int) *TwoSat { return &TwoSat{size: n, graph: make([][]int32, n*2)} } // u -> v <=> !v -> !u func (ts *TwoSat) AddIf(u, v int) { ts.AddDirectedEdge(u, v) ts.AddDirectedEdge(ts.Rev(v), ts.Rev(u)) } // u or v <=> !u -> v func (ts *TwoSat) AddOr(u, v int) { ts.AddIf(ts.Rev(u), v) } // u nand v <=> u -> !v func (ts *TwoSat) AddNand(u, v int) { ts.AddIf(u, ts.Rev(v)) } // 手动添加边(不推荐).常用于优化建图时. func (ts *TwoSat) AddDirectedEdge(u, v int) { ts.graph[u] = append(ts.graph[u], int32(v)) } // u <=> !u -> u func (ts *TwoSat) SetTrue(u int) { ts.AddDirectedEdge(ts.Rev(u), u) } // !u <=> u -> !u func (ts *TwoSat) SetFalse(u int) { ts.AddDirectedEdge(u, ts.Rev(u)) } func (ts *TwoSat) Rev(u int) int { if u >= ts.size { return u - ts.size } return u + ts.size } func (ts *TwoSat) Solve() (res []bool, ok bool) { _, belong := StronglyConnectedComponentInt32(ts.graph) res = make([]bool, ts.size) for i := 0; i < int(ts.size); i++ { if belong[i] == belong[ts.Rev(i)] { return } res[i] = belong[i] > belong[ts.Rev(i)] } ok = true return } // 有向图强连通分量分解. func StronglyConnectedComponentInt32(graph [][]int32) (count int32, belong []int32) { n := int32(len(graph)) belong = make([]int32, n) low := make([]int32, n) order := make([]int32, n) for i := range order { order[i] = -1 } now := int32(0) path := []int32{} var dfs func(int32) dfs = func(v int32) { low[v] = now order[v] = now now++ path = append(path, v) for _, to := range graph[v] { if order[to] == -1 { dfs(to) low[v] = min32(low[v], low[to]) } else { low[v] = min32(low[v], order[to]) } } if low[v] == order[v] { for { u := path[len(path)-1] path = path[:len(path)-1] order[u] = n belong[u] = count if u == v { break } } count++ } } for i := int32(0); i < n; i++ { if order[i] == -1 { dfs(i) } } for i := int32(0); i < n; i++ { belong[i] = count - 1 - belong[i] } return } func min(a, b int) int { if a < b { return a } return b } func max(a, b int) int { if a > b { return a } return b } func min32(a, b int32) int32 { if a < b { return a } return b } func max32(a, b int32) int32 { if a > b { return a } return b } func abs(x int) int { if x < 0 { return -x } return x }