Contenu connexe
Similaire à Permutation (20)
Permutation
- 2. 問題概要
以下の条件を満たす長さ N の数列 X の個数を数
える。
1. 任意の i に対して X[j]=i なる j が存在する。
2. X[s] = t
3. X[a[i]] < X[b[i]] (1≦i≦C)
( ただし i ≠ j ならば a[i] ≠ a[j])
2 ≦ N ≦ 2000
0≦C≦N
- 3. 問題読み替え
条件 1 は単に順列であることを言っているだけ。
簡単のため以下のように問題を読み替える。
2'. X[s] = N-t+1
3'. X[a[i]] > X[b[i]] (1≦i≦C)
( ただし i ≠ j ならば a[i] ≠ a[j] )
もとの条件を満たす数列を X[i] := N – X[i] + 1 で
置き換えた数列の数を数えているに過ぎない
- 4. グラフとして解釈する
各番号を頂点とし、番号 b[i] から a[i] へ辺を張った
頂点数 N のグラフを考える。
このとき、この有向グラフで頂点 s が N-t+1 番目に
くるトポロジカル順序の個数を求めれば良い。
もちろん、 DAG でなければトポロジカル順序が定
義できないので、閉路があれば即座に 0 を返す。
有向閉路の判定は O(N+C) なり O(N*C) なりで簡
単にできる。
- 5. グラフとして解釈する
入次数 1 以下という制限があるので、有向閉路を
持たない場合は根付き木が複数個集まったような
グラフになる。
s
- 6. グラフとして解釈する
連結成分毎に分けて考えてもいいが、ダミーノード
を根としてつけると簡単になる。
この場合、頂点 s が N-t+2 番目にくるようなトポロ
ジカル順序の個数を求めれば良い。
root
s
- 7. ここまでのまとめ
結局、有向閉路を持たない場合は以下のような問
題に帰着できる。
ノード数 N の根付き木が与えられる。このとき、ノー
ド v が k 番目にくるようなトポロジカル順序の個数
を求めよ。
(N は元の問題とは異なることに注意 )
- 8. 解法
動的計画法で解く。
根から v までのパス上で計算する。
以下の解説では番号を 0-indexed にする。
- 9. 根からのパス
このパス上で DP する root
root
v
v
- 10. 状態の関係式
S[u]={u を根とする部分木のサイズ } とする。
S[u] = 1 + Σ S[c]
ただし、 c は u の全ての子
- 11. 状態の関係式
T[u] = {u を根とする部分木のトポロジカル順序の
総数 } とする。
T[u] = (Π T[c]) * (S[u]-1)! / (Π S[c]!)
ただし、 c は u の全ての子
階乗を使わずに二項係数を使って計算しても良い。
- 12. 状態をどのようにとるか
根から v へのパス上のノードを浅い順に V[0]=root,
V[1], …, V[depth]=v, V[d+1]=dummy とする。
S[dummy] = 0, T[dummy] = 1 としておくと楽。
G[i] = {V[i] を根とする部分木 }, G[d+1]= 空とする。
DP[i][j] = {G-G[i+1] で、 V[i] が j 番目に来るような
トポロジカル順序の総数 } とすると、最終的に
DP[depth][k] が答となる。
- 13. 状態の関係式
オーバーフローに注意
DP[i][j] = (Σ_{0≦k<j} DP[i-1][k])
* combination(N-S[V[i+1]]-1-j, S[V[i]]-S[V[i+1]] - 1)
* (Π T[c]) * (S[i]-S[i+1]-1)! / (Π S[c]!)
ただし、 c は V[i] の V[i+1] 以外の子
0 j-1 j j+1 N-S[V[i+1]]-1
V[i]
V[i-1] はこのどこかにある
全部で N-S[V[i+1]] 個 この中から
S[V[i]]-S[V[i+1]]-1 個選ぶ
- 14. 計算量
状態数が depth * N でおよそ N^2 通りある。
ひとつの関係式を真面目に計算したら O(N) 程度
かかって全体で O(N^3) となり TLE しそう。
- 15. 計算量
(Π T[c]) * (S[i]-S[i+1]-1)! / (Π S[c]!) の部分は j に
依存しないので最初に計算しておけばよい。
Σ_{0≦k<j} DP[i-1][k] は DP[i-1][j] を j の小さい方
から加算していけば同じ計算を繰り返さずに済む。
( 和が出てくる DP を高速化する典型手法 )
または、累積和を計算しておいて O(1) で表引きで
きるようにしておく、と考えてもよい。
こうすると、計算量は全体で O(N^2) となる。
- 16. 解答例
宮村
C++ : 139 行 2517 byte
Java: 171 行 3327 byte