[题解] CF1761E Make It Connected

题目大意

给一张无向图,每次操作表现为选择一个点 \(x\),断开所有原来连上的边,连接所有原来断开的边。求最少需要几步使得图联通,并构造方案。

思路

分类考虑。

首先如果这个图本身就是连通的,那显然不用操作

然后考虑把一个点 \(x\) 加入一个连通块,一步对 \(x\) 的操作能使图连通

那么把一个连通块加入另一个连通块呢?

考虑把一个连通块中的点一点点拆下来加到另一个连通块。那么就是拆较小的连通块,把它加到较大的里。

真的必须要这么做吗?

如果你有的是两张完全图,那么必须这么拆。

如果你有的是大于两张的完全图,那么你可以选择一个连通块中任意一点进行操作,再换个连通块选择任意一点进行操作。

来证明一下正确性吧。当你“选择一个连通块中任意一点进行操作”后,这个点会连上其他所有的连通块,并断开与原来连通块的链接。“再换个连通块选择任意一点进行操作”后这个点将与除自己原来的连通块外的所有连通块连通。这时候第三个连通块既与第一个操作的点连上了(连上了除第一个连通块外的所有连通块),又与第二个操作的点连上了(连上了除第二个连通块外的所有连通块),两者求并,就是把整个图连通了。

如果有连通块不是完全图,那么你可以选择对不是完全图的连通块中度数最小的点进行一次操作,这张图就联通了。

先证明一下正确性吧。由于这个连通块不是完全图,所以度数最小的点一定与至少一个同连通块中的点不连通。那么在对这个点进行操作后,能保证这个点仍和原属连通块联通,然后它又连上了其他连通块的点,所以这张图就联通了。

至于为什么选度数最小的点呢?因为度数最小的点能保证一定对。

考虑对一个点操作后这个连通块不连通了,这个点要满足的要求是:是割点,与断开后形成的连通块中的至少一个的所有点都连通。可以证明度数最小的点一定不满足这两个情况。

可以用反证法证明。假设这个点度数最小、是割点、与断开后形成的连通块中的至少一个的所有点都连通。假设这个点的度数为 \(p\),那么与它连接的点的度数至少为 \(p\),那么这个图就是完全图,也就不存在割点了,与假设矛盾。

代码

const int N = 4010;
int t, n, mp[N][N];
vector <int> group[N];
bool vis[N];
void dfs(int x, int id) {
	group[id].emplace_back(x); vis[x] = 1;
	for (int v = 1; v <= n; v++) {
		if (!mp[x][v]) continue;
		if (vis[v]) continue;
		dfs(v, id);
	}
}
int main() {
	t = read();
	for (int I = 1; I <= t; I++) {
		n = read();
		for (int i = 1; i <= n; i++) group[i].clear(), vis[i] = 0;
		for (int i = 1; i <= n; i++) 
			for (int j = 1; j <= n; j++) {
				char c = getchar(); while (c != '0' && c != '1') c = getchar();
				mp[i][j] = c ^ 48;
			}
		int idcnt = 0, p0 = 0, p1 = 0; //p0: 连通块内不和所有点连通的点, p1: 孤点 
		for (int i = 1; i <= n; i++) {
			if (vis[i]) continue;
			dfs(i, ++idcnt);
			for (int x: group[idcnt]) 
				for (int y: group[idcnt]) {
					if (x == y) continue;
					if (mp[x][y] == 0) p0 = x;
				}
			if ((int)group[idcnt].size() == 1) p1 = group[idcnt][0];
		}
		if (idcnt == 1) puts("0");
		else if (p0) printf("1\n%d\n", p0);
		else if (idcnt > 2) {
			if (p1) printf("1\n%d\n", p1);
			else printf("2\n%d %d\n", group[1][0], group[2][0]);
		}
		else {
			if (group[1].size() > group[2].size()) swap(group[1], group[2]);
			printf("%d\n", (int)group[1].size());
			for (int x: group[1]) printf("%d ", x); puts("");
		}
	}
	return 0;
}
posted @ 2022-12-28 15:37  shiranui  阅读(28)  评论(0编辑  收藏  举报
*/