IndiaHacks 2016 - Online Edition E. Bear and Forgotten Tree 2
解法
さっきの問題とよく似ています。
mayokoex.hatenablog.com
まず, 頂点 0 からつながる頂点を列挙します。これが k 個以下だったら impossible です。で, それらの点からスタートして, 0 以外の頂点となるべく連結させていきます。これは, さっきの問題と同じような方法で出来ます(相変わらず (u, v) が繋げないかどうかの判定はたかだか 2 回しかしないので計算量は O(M log M))。
あとは判定ですが, 頂点 0 から他の頂点に伸ばす必要のある最低本数が k 以下であれば良いです。これは, さっきの作業で出来た連結成分の数で求められます。
struct UnionFind { vector<int> par; int n, cnt; UnionFind(const int& x = 0) {init(x);} void init(const int& x) {par.assign(cnt=n=x, -1);} inline int find(const int& x) {return par[x] < 0 ? x : par[x] = find(par[x]);} inline bool same(const int& x, const int& y) {return find(x) == find(y);} inline bool unite(int x, int y) { if ((x = find(x)) == (y = find(y))) return false; --cnt; if (par[x] > par[y]) swap(x, y); par[x] += par[y]; par[y] = x; return true; } inline int count() const {return cnt;} inline int count(int x) {return -par[find(x)];} }; void NO() { cout << "impossible" << endl; exit(0); } set<pii> S; bool ok(int u, int v) { if (u > v) swap(u, v); return S.find(pii(u, v)) == S.end(); } const int MAXN = 300300; bool use[MAXN]; int main() { cin.tie(0); ios::sync_with_stdio(false); int n, m, k; cin >> n >> m >> k; UnionFind uf(n); int cnt = 0; for (int i = 1; i < n; i++) use[i] = true; for (int i = 0; i < m; i++) { int a, b; cin >> a >> b; a--; b--; if (a > b) swap(a, b); if (a == 0) { cnt++; use[b] = false; } S.insert(pii(a, b)); } if (cnt+k > n-1) NO(); set<int> unvisit; for (int i = 1; i < n; i++) unvisit.insert(i); int cur = 1; while (cur < n) { if (!use[cur] || unvisit.find(cur) == unvisit.end()) { cur++; continue; } queue<int> que; que.push(cur); unvisit.erase(cur); while (!que.empty()) { int now = que.front(); que.pop(); for (auto it = unvisit.begin(); it != unvisit.end(); ) { if (ok(*it, now)) { uf.unite(*it, now); que.push(*it); it = unvisit.erase(it); } else it++; } } } if (!unvisit.empty()) NO(); if (uf.count() > k+1) NO(); cout << "possible" << endl; return 0; }