Solution -「牛客多校賽 2020 Round II」Cover the Tree
阿新 • • 發佈:2020-07-14
\(\mathcal{Description}\)
link.
給定 \(n\) 個結點的一棵無根樹,求一個最小的可重疊的路徑集合覆蓋所有樹邊。
\(n\le2\times10^5\)。
\(\mathcal{Solution}\)
非官方做法,歡迎卡我 qwq。
首先特判樹的大小為 \(<3\) 的情況。接著遞迴處理:每次取出當前樹的重心為根,不斷匹配最多葉子子樹與次多葉子子樹的兩片葉子,若某棵子樹未完全匹配,遞迴處理即可。這樣的子樹至多存在一棵。
複雜度 \(\mathcal O(n\log n)\)。
\(\mathcal{Code}\)
#include <queue> #include <cstdio> #include <vector> typedef std::pair<int, int> pii; const int MAXN = 2e5; int n, ecnt, head[MAXN + 5], siz[MAXN + 5], hw[MAXN + 5]; bool vis[MAXN + 5]; std::vector<int> ch, leaf[MAXN + 5]; std::priority_queue<pii> que; std::vector<pii> ans; struct Edge { int to, nxt; } graph[MAXN * 2 + 5]; inline void link ( const int s, const int t ) { graph[++ ecnt] = { t, head[s] }, head[s] = ecnt; } inline void findCent ( const int u, const int f, const int all, int& rt ) { siz[u] = 1, hw[u] = 0; for ( int i = head[u], v; i; i = graph[i].nxt ) { if ( ! vis[v = graph[i].to] && v ^ f ) { findCent ( v, u, all, rt ), siz[u] += siz[v]; if ( hw[u] < siz[v] ) hw[u] = siz[v]; } } hw[u] = hw[u] < all - siz[u] ? all - siz[u] : hw[u]; if ( ! rt || hw[rt] > hw[u] ) rt = u; } inline void collect ( const int u, const int f, const int vid ) { bool isleaf = true; for ( int i = head[u], v; i; i = graph[i].nxt ) { if ( ( v = graph[i].to ) ^ f ) { isleaf = false; if ( ! vis[v] ) collect ( v, u, vid ); } } if ( isleaf ) leaf[vid].push_back ( u ); } inline void solve ( const int fa, const int rt ) { int cnt = 0; vis[rt] = true; for ( int i = head[rt], u; i; i = graph[i].nxt ) { if ( ! vis[u = graph[i].to] ) { collect ( u, rt, ++ cnt ); if ( ! leaf[cnt].empty () ) { ch.push_back ( u ); que.push ( { ( int ) leaf[cnt].size (), cnt } ); } } } if ( ! ch.size () ) return void ( ans.push_back ( { rt, fa } ) ); if ( ch.size () == 1u ) return void ( ans.push_back ( { fa, leaf[1][0] } ) ); while ( que.size () > 1 ) { pii p = que.top (); que.pop (); pii q = que.top (); que.pop (); ans.push_back ( { leaf[p.second].back (), leaf[q.second].back () } ); vis[leaf[p.second].back ()] = vis[leaf[q.second].back ()] = true; leaf[p.second].pop_back (), leaf[q.second].pop_back (); if ( -- p.first ) que.push ( p ); if ( -- q.first ) que.push ( q ); } if ( ! que.empty () ) { int id = que.top ().second, u = ch[id - 1], nrt; findCent ( u, 0, siz[u], nrt = 0 ); que.pop (), ch.clear (), leaf[id].clear (); return solve ( rt, nrt ); } ch.clear (); } int main () { scanf ( "%d", &n ); for ( int i = 1, u, v; i < n; ++ i ) { scanf ( "%d %d", &u, &v ); link ( u, v ), link ( v, u ); } if ( n == 1 ) return puts ( "0" ), 0; if ( n == 2 ) return puts ( "1\n1 2" ), 0; int rt; findCent ( 1, 0, n, rt = 0 ); solve ( 0, rt ); printf ( "%d\n", ( int ) ans.size () ); for ( pii e: ans ) printf ( "%d %d\n", e.first, e.second ); return 0; }