Codeforces 321C Ciel the Commander
問題
n頂点からなる木が与えられる。
木のそれぞれの頂点に、以下のような条件を満たすように'A'から'Z'の一文字のラベルを割り当てる。
同じラベルを持つ任意の頂点u, vについて、uからvへのパス上に、uのラベルよりも
小さいアルファベットのラベルを持つ頂点が存在する。
このような割り当てが存在するなら、そのような割り当てをどれか一つ具体的に出力せよ。
そうでない場合はImpossible!と出力せよ。
制約条件
n≦10000
方針
まず、明らかにAのラベルは一つしかおくことができない。
Aのラベルを置いたノードを取り除くと、木はいくつかのサブツリーに分かれる。
そのサブツリーで、今度は最も大きいラベルをBとして同じ問題を解けばよい。
ではどの頂点にAのラベルを貼るかであるが、これは木のcentroidに貼れば十分。
木のcentroidとは、その頂点を木から取り除いた際に、
できるサブツリーのうち最大のもののサイズが最も小さくなるような頂点。
centroidを選び続けていくと、その度に木のサイズは半分になるので、
n≦10^5から、分割を20回もしないうちにサイズが1になり、
結局全ての場合にラベルが貼れることがわかる。
centroidは適当に求める。
毎回木DPをしてOKだが、初期化を全部やってしまうとO(n^2)かかってしまうので、
サブツリーの要な部分だけを初期化するようにすればOK.
これで計算量はO(n logn)のはず。
ソースコード
int n, num[100000]; vector<vi> e; char ans[100000]; void init(int c, int p){ num[c] = 1; rep(i, e[c].size()) if(e[c][i] != p && !ans[e[c][i]]){ init(e[c][i], c); num[c] += num[e[c][i]]; } } pi centroid(int c, int p, int n){ pi res(n - num[c], c); rep(i, e[c].size()) if(e[c][i] != p && !ans[e[c][i]]) res.first = max(res.first, num[e[c][i]]); rep(i, e[c].size()) if(e[c][i] != p && !ans[e[c][i]]) res = min(res, centroid(e[c][i], c, n)); return res; } void rec(int c, char ch){ init(c, c); pi p = centroid(c, c, num[c]); c = p.second; ans[c] = ch; rep(i, e[c].size()) if(!ans[e[c][i]]) rec(e[c][i], ch + 1); } int main(){ cin >> n; e.resize(n); rep(i, n - 1){ int a, b; cin >> a >> b; a--; b--; e[a].pb(b); e[b].pb(a); } rec(0, 'A'); rep(i, n) cout << ans[i] << (i == n - 1 ? "\n" : " "); return 0; }