Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions 2SAT/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# クラス TwoSAT

2-SATを解きます。 変数 `x[0], x[1], ⋯, x[N - 1]` に関して、

`(x[i] = f) ∨ (x[j] = g)`

というクローズを足し、これをすべて満たす変数の割当があるかを解きます。

## コンストラクタ

```java
public TwoSAT(int n)
```

`n` 変数の2-SATを作ります。

計算量

$O(n)$

## メソッド

### addClause

```java
public void addClause(int x, boolean f, int y, boolean g)
```

`(x[i] = f) ∨ (x[j] = g)` というクローズを足します。

制約

- `0 <= i < n`
- `0 <= j < n`

計算量

ならし $O(1)$

### addImplication

```java
public void addImplication(int x, boolean f, int y, boolean g)
```

`(x[i] = f) → (x[j] = g)`, 即ち `(x[i] = !f) ∨ (x[j] = g)` というクローズを足します。

制約

- `0 <= i < n`
- `0 <= j < n`

計算量

ならし $O(1)$

### addNand

```java
public void addNand(int x, boolean f, int y, boolean g)
```

`!((x[i] = f) ∧ (x[j] = g))`, 即ち `(x[i] = !f) ∨ (x[j] = !g)` というクローズを足します。禁止制約の追加と考えるとよいです。

制約

- `0 <= i < n`
- `0 <= j < n`

計算量

ならし $O(1)$

### satisfiable

```java
public boolean satisfiable()
```

条件を足す割当が存在するかどうかを判定する。割当が存在するならば `true`、そうでないなら `false` を返す。

制約

- 複数回呼ぶことも可能。

計算量

足した制約の個数を `m` として $O(n+m)$

### answer

```java
public java.util.BitSet answer()
```

`satisfiable` を最後に呼んだ時点での、クローズを満たす割当を返す。割当が存在しなかった場合は `null` を返す。

__`satisfiable` を一度も呼んでいない時点で呼ばれた場合は、実行時例外 `UnsupportedOperationException` が発生します (`satisfiable` の呼び忘れ防止)。__

制約

- __`satisfiable` を少なくとも 1 回は呼んでいる__

計算量

$O(n)$

## 使用例

[AtCoder Library Practice Contest H - Two SAT](https://atcoder.jp/contests/practice2/submissions/16603939)
146 changes: 146 additions & 0 deletions 2SAT/TwoSAT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
class TwoSAT {
private final int n;
private final InternalSCC scc;
private final java.util.BitSet answer;

private boolean hasCalledSatisfiable = false;
private boolean existsAnswer = false;

public TwoSAT(int n) {
this.n = n;
this.scc = new InternalSCC(2 * n);
this.answer = new java.util.BitSet(n);
}

public void addClause(int x, boolean f, int y, boolean g) {
scc.addEdge(x << 1 | (f ? 0 : 1), y << 1 | (g ? 1 : 0));
scc.addEdge(y << 1 | (g ? 0 : 1), x << 1 | (f ? 1 : 0));
}

public void addImplication(int x, boolean f, int y, boolean g) {
addClause(x, !f, y, g);
}

public void addNand(int x, boolean f, int y, boolean g) {
addClause(x, !f, y, !g);
}

public boolean satisfiable() {
hasCalledSatisfiable = true;
int[] ids = scc.ids();
for (int i = 0; i < n; i++) {
if (ids[i << 1 | 0] == ids[i << 1 | 1]) return existsAnswer = false;
answer.set(i, ids[i << 1 | 0] < ids[i << 1 | 1]);
}
return existsAnswer = true;
}

public java.util.BitSet answer() {
if (!hasCalledSatisfiable) {
throw new UnsupportedOperationException(
"Call TwoSAT#satisfiable at least once before TwoSAT#answer."
);
}
if (existsAnswer) return answer;
return null;
}

private static final class EdgeList {
long[] a;
int ptr = 0;
EdgeList(int cap) {a = new long[cap];}
void add(int upper, int lower) {
if (ptr == a.length) grow();
a[ptr++] = (long) upper << 32 | lower;
}
void grow() {
long[] b = new long[a.length << 1];
System.arraycopy(a, 0, b, 0, a.length);
a = b;
}
}

private static final class InternalSCC {
final int n;
int m;
final EdgeList unorderedEdges;
final int[] start;
InternalSCC(int n) {
this.n = n;
this.unorderedEdges = new EdgeList(n);
this.start = new int[n + 1];
}
void addEdge(int from, int to) {
unorderedEdges.add(from, to);
start[from + 1]++;
this.m++;
}
static final long mask = 0xffff_ffffl;
int[] ids() {
for (int i = 1; i <= n; i++) {
start[i] += start[i - 1];
}
int[] orderedEdges = new int[m];
int[] count = new int[n + 1];
System.arraycopy(start, 0, count, 0, n + 1);
for (int i = 0; i < m; i++) {
long e = unorderedEdges.a[i];
orderedEdges[count[(int) (e >>> 32)]++] = (int) (e & mask);
}
int nowOrd = 0;
int groupNum = 0;
int k = 0;
int[] par = new int[n];
int[] vis = new int[n];
int[] low = new int[n];
int[] ord = new int[n];
java.util.Arrays.fill(ord, -1);
int[] ids = new int[n];
long[] stack = new long[n];
int ptr = 0;

for (int i = 0; i < n; i++) {
if (ord[i] >= 0) continue;
par[i] = -1;
stack[ptr++] = i;
while (ptr > 0) {
long p = stack[--ptr];
int u = (int) (p & mask);
int j = (int) (p >>> 32);
if (j == 0) {
low[u] = ord[u] = nowOrd++;
vis[k++] = u;
}
if (start[u] + j < count[u]) {
int to = orderedEdges[start[u] + j];
stack[ptr++] += 1l << 32;
if (ord[to] == -1) {
stack[ptr++] = to;
par[to] = u;
} else {
low[u] = Math.min(low[u], ord[to]);
}
} else {
while (j --> 0) {
int to = orderedEdges[start[u] + j];
if (par[to] == u) low[u] = Math.min(low[u], low[to]);
}
if (low[u] == ord[u]) {
while (true) {
int v = vis[--k];
ord[v] = n;
ids[v] = groupNum;
if (v == u) break;
}
groupNum++;
}
}
}
}
for (int i = 0; i < n; i++) {
ids[i] = groupNum - 1 - ids[i];
}
return ids;
}
}
}
73 changes: 73 additions & 0 deletions SCC/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# クラス SCC (Strongly Connected Components: 強連結成分分解)

有向グラフを強連結成分分解します。

## コンストラクタ

```java
SCC(int n)
```

`n` 頂点 `0` 辺の有向グラフを作る。

計算量

$O(n)$

## メソッド

### addEdge

```java
public void addEdge(int from, int to)
```

頂点 `from` から頂点 `to` へ有向辺を足す。

制約

- `0 <= from < n`
- `0 <= to < n`

計算量

amortized $O(1)$

### build

```java
public int[][] build()
```

以下の条件を満たすような、「頂点の配列」の配列を返します。

- 全ての頂点がちょうど 1 つずつ、どれかのリストに含まれます。
- 内側のリストと強連結成分が一対一に対応します。リスト内での頂点の順序は未定義です。
- 配列はトポロジカルソートされています。異なる強連結成分の頂点 `u`, `v` について、`u` から `v` に到達できる時、`u` の属する配列は `v` の属する配列よりも前です。

計算量

追加した辺の本数を `m` として $O(n + m)$

### id

```java
public int id(int i)
```

頂点 `i` が (トポロジカル順序において) 何番目の強連結成分に属しているかを返します。即ち,`build` で得られる「頂点の配列」の配列を `g` とすると、`g[id(i)][j] = i` なる `j` が存在します。

__ただし、`build` を呼ぶ前にこのメソッドが呼ばれた場合は、実行時例外 `UnsupportedOperationException` が発生します。__

制約

- __`build` メソッドを既に呼んでいる__
- `0 <= i < n`

計算量

$O(1)$

## 使用例

[AtCoder Library Practice Contest G - SCC](https://atcoder.jp/contests/practice2/submissions/16603978)
Loading