CodeForces 1916F Group Division

发布时间 2024-01-01 16:03:02作者: zltzlt

洛谷传送门

CF 传送门

考虑增量构造第一个集合。首先令 \(S = \{1\}\),然后不断找到下一个点 \(u\),使得它在抠掉 \(S\) 的图上不是割点,并且与 \(S\) 连通。然后令 \(S \gets S \cup \{u\}\)

可以证明一定能找到这样的 \(u\)

因为对于抠掉 \(S\) 的新图上,若存在割点 \(v\),那么删除 \(v\) 后的每一个连通块都与 \(S\) 连通,否则与题目原图没有割点的条件矛盾。

所以我们能找到一个割点 \(w\) 使得 dfs 树上它存在一棵子树没有割点。由上一定能在这棵子树找到一个与 \(S\) 连通的点。

综上所述一定能找到这样的 \(u\)

时间复杂度 \(O(n(n + m))\)

code
// Problem: F. Group Division
// Contest: Codeforces - Good Bye 2023
// URL: https://codeforces.com/contest/1916/problem/F
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 4040;

int n, n1, n2, m;
bool vis[maxn], cut[maxn];
vector<int> G[maxn];

int low[maxn], dfn[maxn], tim;

void dfs(int u, int fa) {
	low[u] = dfn[u] = ++tim;
	cut[u] = 0;
	int cnt = 0;
	for (int v : G[u]) {
		if (v == fa || vis[v]) {
			continue;
		}
		if (!dfn[v]) {
			dfs(v, u);
			++cnt;
			low[u] = min(low[u], low[v]);
			if (low[v] >= dfn[u]) {
				cut[u] = 1;
			}
		} else {
			low[u] = min(low[u], dfn[v]);
		}
	}
	if (cut[u] && fa == -1 && cnt == 1) {
		cut[u] = 0;
	}
}

void solve() {
	scanf("%d%d%d", &n1, &n2, &m);
	n = n1 + n2;
	for (int i = 1; i <= n; ++i) {
		vector<int>().swap(G[i]);
		vis[i] = 0;
	}
	while (m--) {
		int u, v;
		scanf("%d%d", &u, &v);
		G[u].pb(v);
		G[v].pb(u);
	}
	vis[1] = 1;
	for (int _ = 1; _ < n1; ++_) {
		for (int i = 1; i <= n; ++i) {
			low[i] = dfn[i] = 0;
		}
		for (int i = 1; i <= n; ++i) {
			if (vis[i]) {
				continue;
			}
			tim = 0;
			dfs(i, -1);
			break;
		}
		for (int i = 1; i <= n; ++i) {
			if (vis[i] || cut[i]) {
				continue;
			}
			bool fl = 0;
			for (int j : G[i]) {
				fl |= vis[j];
			}
			if (fl) {
				vis[i] = 1;
				break;
			}
		}
	}
	for (int i = 1; i <= n; ++i) {
		if (vis[i]) {
			printf("%d ", i);
		}
	}
	putchar('\n');
	for (int i = 1; i <= n; ++i) {
		if (!vis[i]) {
			printf("%d ", i);
		}
	}
	putchar('\n');
}

int main() {
	int T = 1;
	scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}