1. 程式人生 > 實用技巧 >Solution -「牛客多校賽 2020 Round II」Cover the Tree

Solution -「牛客多校賽 2020 Round II」Cover the Tree

\(\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;
}