面白かった!
問題概要
頂点数が の木であって、各頂点 から最も遠い頂点への距離がそれぞれ であるような木が存在するかどうかを判定せよ。
制約
考えたこと
面白そう!!!!!!似た見た目の問題としては、次の問題もあった。
でもちょっと系統は違いそう。とりあえずあーだこーだ考えてみる。「木」についての問題で、距離について考察する問題なので、とりあえず「直径」に着目したくなる。
たとえば、数列が (3, 4, 4, 4, 4, 5, 5, 6, 6, 6) の場合は、最遠が 6 なので直径が 6 だとわかる。するとまずは下図のようになる!さらに言えば、直径上の「最遠距離ラベル」もすべて下図のように確定する!
なぜならば、直径上の頂点に対して、下図の距離ラベルよりも遠い頂点が存在すると仮定すると、「直径よりも長いパス」がとれることになって矛盾するのだ。
よってこの時点で
- 2 以下は存在したらダメ
- 3 は存在しないとダメ
- 4 は 2 個以上ないとダメ
- 5 は 2 個以上ないとダメ
- 6 は 2 個以上ないとダメ
ということもわかる。逆に 4, 5, 6 については 2 個より多くあっても、下図のようにいくらでも追加できるので OK。
注意したいこととして、「3」は個数を増やせないことに注意。一般に
- 直径 が偶数のとき、距離が の頂点はちょうど 1 個であることが必要
- 直径 が奇数のとき、距離が の頂点はちょうど 2 個であることが必要
と言える。計算量は 。
コード
#include <bits/stdc++.h> using namespace std; bool solve(vector<int> a) { int N = (int)a.size(); sort(a.begin(), a.end(), greater<int>()); int vmax = a[0], vmin = a.back(); if (vmax > N-1) return false; if (vmin < (vmax + 1) / 2) return false; map<int,int> ma; for (auto v : a) ma[v]++; if (vmax % 2 == 0 && ma[vmin] != 1) return false; if (vmax % 2 == 1 && ma[vmin] != 2) return false; for (int v = vmin + 1; v <= vmax; ++v) { if (ma[v] < 2) return false; } return true; } int main() { int N; cin >> N; vector<int> a(N); for (int i = 0; i < N; ++i) cin >> a[i]; cout << (solve(a) ? "Possible" : "Impossible") << endl; }