ホーム BOJ. Josephus problem(2) (1168)
記事
キャンセル

BOJ. Josephus problem(2) (1168)

問題: BOJ 1168 — ヨセフス問題 2

1番からN番まで番号の付いた人が円形に並んでいます。次の人から数えてK番目の人を順に取り除き、取り除いた順序を<a, b, ...>の形式で出力します。

生存者数を管理するFenwick tree

各位置には、その番号の人がまだ円にいれば1、取り除かれていれば0を保持します。Fenwick treeの接頭辞和から、その位置までの生存者数が分かります。この木を使うと、1人の削除と現在の生存者順位に対応する人の検索をそれぞれO(log N)で行えます。

rankは次に数える人の、生存者内での0始まりの順位、remainingは生存者数です。このラウンドで取り除く人の順位は(rank + K - 1) % remainingです。Fenwick treeの二分探索で1始まりの順位rank + 1に対応する元の番号を求め、その位置の値を減らします。削除後、次の計数はその次の生存者から始まるため、新しい円での開始順位はrank % (remaining - 1)です。最後の1人を取り除くときはこの更新を行わず、0による剰余を避けます。N = 1なら順位0の1人だけが出力され、結果は<1>となります。

各人の削除ごとに順位検索と更新を行うため、時間計算量はO(N log N)、空間計算量はO(N)です。

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import java.io.BufferedInputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        FastScanner input = new FastScanner();
        int n = input.nextInt();
        int k = input.nextInt();

        FenwickTree live = new FenwickTree(n);
        StringBuilder output = new StringBuilder("<");
        int rank = 0;

        for (int remaining = n; remaining > 0; remaining--) {
            rank = (int) ((rank + (long) k - 1) % remaining);
            int person = live.findByOrder(rank + 1);
            live.add(person, -1);
            if (remaining > 1) {
                rank %= remaining - 1;
            }

            if (output.length() > 1) {
                output.append(", ");
            }
            output.append(person);
        }
        output.append('>');
        System.out.print(output);
    }

    private static class FenwickTree {
        private final int size;
        private final int[] tree;
        private final int highestPowerOfTwo;

        FenwickTree(int size) {
            this.size = size;
            this.tree = new int[size + 1];
            for (int i = 1; i <= size; i++) {
                tree[i] = i & -i;
            }

            int power = 1;
            while (power <= size / 2) {
                power <<= 1;
            }
            highestPowerOfTwo = power;
        }

        void add(int index, int delta) {
            for (int i = index; i <= size; i += i & -i) {
                tree[i] += delta;
            }
        }

        int findByOrder(int order) {
            int index = 0;
            for (int step = highestPowerOfTwo; step > 0; step >>= 1) {
                int next = index + step;
                if (next <= size && tree[next] < order) {
                    index = next;
                    order -= tree[next];
                }
            }
            return index + 1;
        }
    }

    private static class FastScanner {
        private final BufferedInputStream input = new BufferedInputStream(System.in);

        int nextInt() throws IOException {
            int c;
            do {
                c = input.read();
            } while (c <= ' ' && c != -1);

            int value = 0;
            while (c > ' ') {
                value = value * 10 + c - '0';
                c = input.read();
            }
            return value;
        }
    }
}
この記事は著者により CC BY 4.0 ライセンスで公開されています。