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
7 changes: 5 additions & 2 deletions LazySegTree/LazySegTree.java
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,11 @@ public S allProd() {
}

public void apply(int p, F f) {
Dat[p] = Mapping.apply(f, get(p));
updateFrom(p + N);
exclusiveRangeCheck(p);
p += N;
pushTo(p);
Dat[p] = Mapping.apply(f, Dat[p]);
updateFrom(p);
}

public void apply(int l, int r, F f) {
Expand Down
210 changes: 210 additions & 0 deletions MaxFlow/MaxFlow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
class MaxFlow {
public class CapEdge {
private final int from, to;
private long cap;
private final int rev;
CapEdge(int from, int to, long cap, int rev) {
this.from = from;
this.to = to;
this.cap = cap;
this.rev = rev;
}
public int getFrom() {return from;}
public int getTo() {return to;}
public long getCap() {return cap;}
public long getFlow() {return g[to][rev].cap;}
}

private static final long INF = Long.MAX_VALUE;

private final int n;
private int m;
private final java.util.ArrayList<CapEdge> edges;
private final int[] count;
private final CapEdge[][] g;

public MaxFlow(int n) {
this.n = n;
this.edges = new java.util.ArrayList<>();
this.count = new int[n];
this.g = new CapEdge[n][];
}

public int addEdge(int from, int to, long cap) {
vertexRangeCheck(from);
vertexRangeCheck(to);
capNonNegativeCheck(cap);
CapEdge e = new CapEdge(from, to, cap, count[to]);
count[from]++; count[to]++;
edges.add(e);
return m++;
}

public CapEdge getEdge(int i) {
edgeRangeCheck(i);
return edges.get(i);
}

public java.util.ArrayList<CapEdge> getEdges() {
return edges;
}

public void changeEdge(int i, long newCap, long newFlow) {
edgeRangeCheck(i);
capNonNegativeCheck(newCap);
if (newFlow > newCap) {
throw new IllegalArgumentException(
String.format("Flow %d is greater than capacity %d.", newCap, newFlow)
);
}
CapEdge e = edges.get(i);
CapEdge er = g[e.to][e.rev];
e.cap = newCap - newFlow;
er.cap = newFlow;
}

private void buildGraph() {
for (int i = 0; i < n; i++) {
g[i] = new CapEdge[count[i]];
}
int[] idx = new int[n];
for (CapEdge e : edges) {
g[e.to][idx[e.to]++] = new CapEdge(e.to, e.from, 0, idx[e.from]);
g[e.from][idx[e.from]++] = e;
}
}

public long maxFlow(int s, int t) {
return flow(s, t, INF);
}

public long flow(int s, int t, long flowLimit) {
vertexRangeCheck(s);
vertexRangeCheck(t);
buildGraph();
long flow = 0;
int[] level = new int[n];
int[] que = new int[n];
int[] iter = new int[n];
while (true) {
java.util.Arrays.fill(level, -1);
dinicBFS(s, t, level, que);
if (level[t] < 0) return flow;
java.util.Arrays.fill(iter, 0);
while (true) {
long d = dinicDFS(t, s, flowLimit - flow, iter, level);
if (d <= 0) break;
flow += d;
}
}
}

private void dinicBFS(int s, int t, int[] level, int[] que) {
int hd = 0, tl = 0;
que[tl++] = s;
level[s] = 0;
while (tl > hd) {
int u = que[hd++];
for (CapEdge e : g[u]) {
int v = e.to;
if (e.cap <= 0 || level[v] >= 0) continue;
level[v] = level[u] + 1;
if (v == t) return;
que[tl++] = v;
}
}
}

private long dinicDFS(int cur, int s, long f, int[] iter, int[] level) {
if (cur == s) return f;
long res = 0;
while (iter[cur] < count[cur]) {
CapEdge er = g[cur][iter[cur]++];
int u = er.to;
CapEdge e = g[u][er.rev];
if (level[u] >= level[cur] || e.cap <= 0) continue;
long d = dinicDFS(u, s, Math.min(f - res, e.cap), iter, level);
if (d <= 0) continue;
e.cap -= d;
er.cap += d;
res += d;
if (res == f) break;
}
return res;
}

public long fordFulkersonMaxFlow(int s, int t) {
return fordFulkersonFlow(s, t, INF);
}

public long fordFulkersonFlow(int s, int t, long flowLimit) {
vertexRangeCheck(s);
vertexRangeCheck(t);
buildGraph();
boolean[] used = new boolean[n];
long flow = 0;
while (true) {
java.util.Arrays.fill(used, false);
long f = fordFulkersonDFS(s, t, flowLimit - flow, used);
if (f <= 0) return flow;
flow += f;
}
}

private long fordFulkersonDFS(int cur, int t, long f, boolean[] used) {
if (cur == t) return f;
used[cur] = true;
for (CapEdge e : g[cur]) {
if (used[e.to] || e.cap <= 0) continue;
long d = fordFulkersonDFS(e.to, t, Math.min(f, e.cap), used);
if (d <= 0) continue;
e.cap -= d;
g[e.to][e.rev].cap += d;
return d;
}
return 0;
}

public boolean[] minCut(int s) {
vertexRangeCheck(s);
boolean[] reachable = new boolean[n];
int[] stack = new int[n];
int ptr = 0;
stack[ptr++] = s;
reachable[s] = true;
while (ptr > 0) {
int u = stack[--ptr];
for (CapEdge e : g[u]) {
int v = e.to;
if (reachable[v] || e.cap <= 0) continue;
reachable[v] = true;
stack[ptr++] = v;
}
}
return reachable;
}

private void vertexRangeCheck(int i) {
if (i < 0 || i >= n) {
throw new IndexOutOfBoundsException(
String.format("Index %d out of bounds for length %d", i, n)
);
}
}

private void edgeRangeCheck(int i) {
if (i < 0 || i >= m) {
throw new IndexOutOfBoundsException(
String.format("Index %d out of bounds for length %d", i, m)
);
}
}

private void capNonNegativeCheck(long cap) {
if (cap < 0) {
throw new IllegalArgumentException(
String.format("Capacity %d is negative.", cap)
);
}
}
}
133 changes: 133 additions & 0 deletions MaxFlow/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# クラス MaxFlow

## コンストラクタ

```java
public MaxFlow(int n)
```

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

計算量

$O(n)$

## メソッド

### addEdge

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

`from` から `to` へ最大容量 `cap`、流量 0 の辺を追加し、何番目に追加された辺かを返す。

制約

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

計算量

ならし $O(1)$

### maxFlow

```java
// (1)
public long maxFlow(int s, int t)
// (2)
public long flow(int s, int t, long flowLimit)
// (3)
public long fordFulkersonMaxFlow(int s, int t)
// (4)
public long fordFulkersonFlow(int s, int t, long flowLimit)
```

- (1): 頂点 `s` から `t` へ流せる限り流し、流せた量を返す。Dinic のアルゴルズムを用います。
- (2) 頂点 `s` から `t` へ流量 `flowLimit` に達するまで流せる限り流し、流せた量を返す。Dinic のアルゴルズムを用います。
- (3) 頂点 `s` から `t` へ流せる限り流し、流せた量を返す。FordFulkerson のアルゴルズムを用います。
- (4) 頂点 `s` から `t` へ流量 `flowLimit` に達するまで流せる限り流し、流せた量を返す。FordFulkerson のアルゴルズムを用います。
- 複数回呼んだ場合は,前回流したフローからの差分が返ります。

制約

- `0 <= s, t < n`

計算量

`m` を追加された辺数として

- (1), (2) $O(\min(n^{2/3}*m, m^{3/2}))$ (辺の容量がすべて 1 の時)
- (1), (2) $O(n^2*m)$
- (3), (4) 返り値を `f` として,`O(f*m)`

### minCut

```java
public boolean[] minCut(int s)
```

長さ `n` の `boolean` 配列を返す。`i` 番目の要素は、頂点 `s` から `i` へ残余グラフで到達可能なとき、またその時のみ `true`。maxFlow(s, t) をちょうど一回呼んだ後に呼ぶと、返り値は `s`, `t` 間の mincut に対応します。

計算量

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

### getEdge / getEdges

`getEdge`、`getEdges` の返り値には以下のメソッドを持つ `MaxFlow.CapEdge` 型が用いられています。

```java
class CapEdge {
// (1)
public int getFrom()
// (2)
public int getTO()
// (3)
public int getCap()
// (4)
public int getFlow()
};
```

- (1): 始点を返します。計算量: $O(1)$
- (2): 終点を返します。計算量: $O(1)$
- (3): 現在の容量を返します。計算量: $O(1)$
- (4): 現在の流量を返しまず。計算量: $O(1)$

```java
// (1)
public CapEdge getEdge(int i)
// (2)
public java.util.ArrayList<CapEdge> getEdges()
```

今の内部の辺の状態を返す。辺の順番は `addEdge` で追加された順番と同一。

制約

- (1): 追加した辺の数を `m` として,`0 <= i < m`。

計算量

`m` を追加された辺数として

- (1): $O(1)$
- (2): $O(1)$

### changeEdge

```java
public void changeEdge(int i, long newCap, long newFlow)
```

`i` 番目に変更された辺の容量、流量を `newCap`, `newFlow` に変更する。他の辺の容量、流量は変更しない。

制約

- `0 <= newFlow <= newCap`

計算量

$O(1)