競技プログラミング用の,順列組合せ (重複有り無し) を生成する自作 C++ ライブラリ Perm の使用法です.ソースコードはここ にあります.

struct

以下の struct がある.名前がおかしいのは歴史的理由による.

  • IntPerm 順列
  • IntComb 組合せ
  • IntDupPerm 重複順列
  • IntDupComb 重複組合せ
  • IntDirProd 直積
  • IntPartition 分割数

constructor

IntPerm ip(m, n);         // m から n 取る順列
IntComp ic(m, n);         // m から n 取る組合せ
IntDupPerm idp(m, n);     // m から n 取る重複順列
IntDupComb idc(m, n);     // m から n 取る重複組合せ
IntDirProd did(vector<int>{{m1, m2, ...}})   // [0:m1) × [0:m2) × ...
IntPartition part(m)      // m の分割

最初の4つは,$[0, m)$ の数からなる長さ $n$ のリストを生成する. このライブラリでは,「順列」と「組合せ」の違いは,後者は昇順にソートされている,ということである.

IntPerm

$[0, m)$ からなる長さ $n$ のリストで,同じ数がたかだか1回しか現れないものを列挙する. たとえば ip(4, 2) は,次を生成する:

[0,1], [0,2], [0,3], [0,4], [1,0], [1,1], [1,2], [1,3], [2,0], [2,1], [2,2], [2,3], [3,0], [3,1], [3,2], [3,3]

IntComb

$[0, m)$ からなる長さ $n$ のリストで,同じ数がたかだか1回しか現れず,昇順ソートされているものを列挙する. たとえば,ic(4, 2) は,次を生成する:

[0,1], [0,2], [0,3], [0,4], [1,2], [1,3], [2,3]

IntDupPerm

$[0, m)$ からなる長さ $n$ のリストを列挙する. たとえば idp(4, 2) は,次を生成する:

[0,0], [0,1], [0,2], [0,3], [1,0], [1,1], [1,2], [1,3], [2,0], [2,1], [2,2], [2,3], [3,0], [3,1], [3,2], [3,3]

IntDupComb

$[0, m)$ からなる長さ $n$ のリストで,昇順ソートされているものを列挙する. たとえば idc(4, 2) は,次を生成する:

[0,0], [0,1], [0,2], [0,3], [1,1], [1,2], [1,3], [2,2], [2,3], [3,3]

後述の「ボールと箱の重複組合せ」も参照のこと.

IntDirProd

$[0, m_1) \times [0, m_2) \times \dots$ の要素を列挙する. たとえば,did(vector{{1, 2, 3}}) は,次を生成する:

[0,0,0],[0,0,1],[0,0,2],[0,1,0],[0,1,1],[0,1,2]

IntPartition

分割を生成する.たとえば,part(5) は,次を生成する.

[[1,1,1,1,1],[1,1,1,2],[1,1,3],[1,2,2],[1,4],[2,3],[5]]

生成

obj.get() が true を返す間,新しいリストが生成される. リストの i 番目の要素は,obj.at(i) である.obj[i] でも良い.

while (ip.get()) {
  for (int i = 0; i < 2; i++) cout << ip.at(i) << " ";
}

主としてデバッグ用に,obj.vec_view() も用意されており,const vector<int>& を返す.

const vector<int>& v = ip.vec_view();
for (int i = 0; i < 2; i++) cout << v[i] << " ";

注意1

obj.get() が false を返すときには,内部状態は初期値に戻るので,そのまま次のラウンドを実行することができる.

注意2

  • $n \geq 0, r \geq 0$ でないときには,何も生成しない.
  • IntPerm と IntComb は,$r \leq n$ でないときには,何も生成しない.
  • IntDupPerm と IntDupComb は,$n = r = 0$ のときに,何も生成しないのでは なく,空リスト [] を生成する.これは,ことによると適切でないかもしれない.($0^0 = 1$ になるし,$H(n, r) \neq C(n+r-1, r)$ になるから)
  • $n = 0$ かつ $r \neq 0$ の場合には,何も生成しない.

デバッグ用

const vector<int>& obj.vec_view(); でベクトルに変換できる.

[0, 1, ..., n) 以外の対象

コンストラクタの第3引数に,長さ $m$ のベクトルを置いて,そこから取ってこさせることができる:

  IntComb ip(4, 2, vector{{100, 50, 90, 10}});
  // [[100, 50], [100, 90], [100, 10], [50, 90], [50, 10], [90, 10]]
  IntPerm<char> ip(3, 2, vector<char>{'a', 'b', 'c'});
  // [['a', 'b'], ['a', 'c'], ['b', 'a'], ['b', 'c'], ['c', 'a'], ['c', 'b']]

メソッド set_mapping を使うこともできる.

  IntPerm<char> ip(3, 2);
  ip.set_mapping([](int i) { return "abc"[i]; });
  // 上と同じ

その他

ボールと箱の重複組合せ

区別される $m$ 個の箱があって,そこに区別されない $n$ 個のボールを入れることを考える状況がよくある. これは,$m$ から重複を許して $n$ だけ選ぶ,ということになる. たとえば,$m = 3$ 個の箱 A, B, C があって,そこに $n = 5$ 個のボールをいれて,おのおの 2個,1個,2個入った,ケースを考えよう.これは,このライブラリで言えば,箱 A, B, C に識別番号 0, 1, 2 がついていて,5回の選択で [0, 0, 1, 2, 2] が選ばれた,ということに対応している.

しかし,このような状況では,普通,各々の箱にボールがいくつあるか,ということに興味があろう.上のケースでは [2, 1, 2] を生成してほしいわけである.これは,次のコードで可能になる:

IntDupComb idc(m, n);
while (idc.get()) {
  vector<ll> numBalls(m);
  REP(i, 0, n) numBalls[idc.at(i)]++;
  //  箱 j にはボールが numBalls[j] 個入っている
}

分割数

  • 10000 までの分割数の表は,https://oeis.org/A000041/b000041.txt にある.

  • 最初のほうのいくつかは次の通り:

// 0  1  2  3  4  5   6   7   8   9  10  11  12   13   14   15   16   17   18   19    //  i
[  1, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42, 56, 77, 101, 135, 176, 231, 297, 385, 490]   // p(i)
p(20) = 627
p(25) = 1958
p(30) = 5604
p(40) = 37338 (3.7E4)
p(50) = 204226 (2.0E5)
p(61) = 1121505 (1.1E6)
p(77) = 10619863 (1.1E7)