TopCoder SRM 581 Div1 Medium TreeUnion
問題
それぞれn頂点からなる木A, Bが与えられる。
この二つの木を次のようにしてつなぐ。
- 0, 1, ..., n - 1の順列をP[i]とする。
- A[i]とB[P[i]]の間に辺を張る。
出来たグラフに大きさKの単純閉路(同じ点を二度通らない閉路)はいくつできるか。
全ての順列が等しい確率で選ばれるとき、その期待値を求めよ。
制約条件
n≦300
3≦K≦7
方針
長さ3の閉路は絶対にできない。
Aの頂点→Bの頂点→Aの頂点→Bの頂点という風に、頂点を行き来するような閉路も、
A[i]とB[j]の間には高々1本しか辺がないので、K≦7では出来ない。
なので、Aのある線分(a, a')とBのある線分(b, b')をつないだような閉路だけを考えればよい。
Aの部分の長さは、1, 2, 3, ..., K - 3がありえる。
A, Bのそれぞれにおいて、頂点iから距離dのところにある頂点の個数をあらかじめ求めておく。
Aの線分(a, a')を一つ決めるとき、長さKの閉路がいくつできるか考える。
bを一つ決める。すると、b'の候補は、前計算により何個かわかっている。
a'のBにおけるペアが、b'の候補のどれかであれば長さKの閉路ができる。
この確率は、|b'| / (n - 1)
全てのbについて、この確率を1 / nして足し合わせればよい。
更にそれを、全てのaについて足し合わせる。
vector<vi> parse(const vector<string> &v){ string s; rep(i, v.size()) s += v[i]; stringstream ss(s); vi a; int x; while(ss >> x) a.pb(x); vector<vi> e(a.size() + 1); rep(i, a.size()){ e[i + 1].pb(a[i]); e[a[i]].pb(i + 1); } return e; } void rec(const vector<vi> &e, int c, int p, int d, vi &res){ if(d) rep(i, e[c].size()) if(e[c][i] != p){ if(d == 1) res.pb(e[c][i]); else rec(e, e[c][i], c, d - 1, res); } } int enu(const vector<vi> &e, int s, int d){ vi res; rec(e, s, s, d, res); return res.size(); } class TreeUnion { public: double expectedCycles(vector <string> tree1, vector <string> tree2, int K) { vector<vi> a = parse(tree1), b = parse(tree2); int n = a.size(); vector<vi> ps1(n, vi(K - 3)), ps2(n, vi(K - 3)); for(int i = 1; i < K - 2; i++){ rep(j, n) ps1[j][i - 1] = enu(a, j, i); rep(j, n) ps2[j][i - 1] = enu(b, j, i); } double ans = 0; rep(j, K - 3) rep(i, n) rep(k, n){ ans += ps1[i][j] * 1.0 / n * ps2[k][K - 4 - j] / (n - 1); } return ans / 2; } };