1. 程式人生 > 實用技巧 >Solution -「CF 1391E」Pairs of Pairs

Solution -「CF 1391E」Pairs of Pairs

\(\mathcal{Description}\)

  Link.

  給定一個 \(n\) 個點 \(m\) 條邊的無向圖,在其上找到一條包括不少於 \(\lceil\frac{n}2\rceil\) 個結點的簡單路徑;或者將至少 \(\lceil\frac{n}2\rceil\) 個結點劃分為若干二元組,使得任意兩個不同二元組內四個結點的匯出子圖含有至多兩條邊。多組資料。

  \(n,\sum n\le5\times10^5\)\(m,\sum m\le10^6\)

\(\mathcal{Solution}\)

  去分開剛兩個 NP 問題,請。

  這種給兩個問題讓你解其中一個的,顯然以嘗試求解一個失敗為條件

來求解另一個,不可能分開解的。比如本題,隨便選一個起點 DFS 全圖,若發現深度不小於 \(\lceil\frac{n}2\rceil\) 的 DFS 樹上結點,回答第一問,結束。否則,問題轉化為:一個圖的 DFS 滿足樹深 \(<\lceil\frac{n}2\rceil\),求解第二問。

  由於 DFS 樹沒有橫叉邊,所以在樹上某一結點的左子樹選一個,右子樹選一個來組成一對,這兩對間的匯出子圖顯然不超過兩條邊。所以用 std::vector 記錄子樹內匹配剩下的結點,在父親處與兄弟子樹剩下的結點配對即可。可以證明在樹深 \(<\lceil\frac{n}2\rceil\) 的情況下一定有解。

\(\mathcal{Code}\)

/* Clearink */

#include <cstdio>
#include <vector>
#include <cstdlib>
#include <assert.h>

inline int rint () {
	int x = 0, f = 1; char s = getchar ();
	for ( ; s < '0' || '9' < s; s = getchar () ) f = s == '-' ? -f : f;
	for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
	return x * f;
}

template<typename Tp>
inline void wint ( Tp x ) {
	if ( x < 0 ) putchar ( '-' ), x = ~ x + 1;
	if ( 9 < x ) wint ( x / 10 );
	putchar ( x % 10 ^ '0' );
}

const int MAXN = 5e5, MAXM = 1e6;
int n, m, ecnt, head[MAXN + 5], p[MAXN + 5];
bool vis[MAXN + 5], solved;
std::vector<int> path, rest[MAXN + 5];
std::vector<std::pair<int, int> > pairs;

struct Edge { int to, nxt; } graph[MAXM * 2 + 5];

inline void link ( const int s, const int t ) {
	graph[++ ecnt] = { t, head[s] };
	head[s] = ecnt;
}

inline void clear () {
	ecnt = solved = 0, path.clear (), pairs.clear ();
	for ( int i = 1; i <= n; ++ i ) head[i] = vis[i] = 0, rest[i].clear ();
}

inline void printPath () {
	solved = true;
	printf ( "PATH\n%d", ( int ) path.size () );
	for ( int i = 0; i ^ path.size (); ++ i ) {
		printf ( "%c%d", i ? ' ' : '\n', path[i] );
	}
	putchar ( '\n' );
}

inline void printPairs () {
	solved = true;
	printf ( "PAIRING\n%d\n", ( int ) pairs.size () );
	for ( auto p: pairs ) printf ( "%d %d\n", p.first, p.second );
}

inline void solve ( const int u ) {
	if ( solved ) return ;
	vis[u] = true, path.push_back ( u ), p[u] = u;
	if ( ( int ) path.size () << 1 >= n ) printPath ();
	for ( int i = head[u], v; i; i = graph[i].nxt ) {
		if ( !vis[v = graph[i].to] ) {
			solve ( v ), path.pop_back ();
			if ( solved ) return ;
			while ( !rest[p[u]].empty () && !rest[p[v]].empty () ) {
				pairs.push_back ( { rest[p[u]].back (), rest[p[v]].back () } );
				if ( ( int ) pairs.size () << 2 >= n ) return printPairs ();
				rest[p[u]].pop_back (), rest[p[v]].pop_back ();
			}
			p[u] = rest[p[u]].empty () ? p[v] : p[u];
		}
	}
	rest[p[u]].push_back ( u );
}

int main () {
	for ( int T = rint (); T --; ) {
		n = rint (), m = rint (), clear ();
		for ( int i = 1, u, v; i <= m; ++ i ) {
			u = rint (), v = rint ();
			link ( u, v ), link ( v, u );
		}
		solve ( 1 );
		assert ( solved );
	}
	return 0;
}

\(\mathcal{Details}\)

  std::vector 的操作能少壓入就少壓入啊……不知道怎麼記憶體就掛掉了。