Codeforces Round #649 (Div.2) D (LCA找简单环)

D. Ehab's Last Corollary

time limit per test

1 second

memory limit per test

256 megabytes

input

standard input

output

standard output

Given a connected undirected graph with nn vertices and an integer kk, you have to either:

  • either find an independent set that has exactly ⌈k2⌉⌈k2⌉ vertices.
  • or find a simple cycle of length at most kk.

An independent set is a set of vertices such that no two of them are connected by an edge. A simple cycle is a cycle that doesn't contain any vertex twice.

I have a proof that for any input you can always solve at least one of these problems, but it's left as an exercise for the reader.

Input

The first line contains three integers nn, mm, and kk (3≤k≤n≤1053≤k≤n≤105, n−1≤m≤2⋅105n−1≤m≤2⋅105) — the number of vertices and edges in the graph, and the parameter kk from the statement.

Each of the next mm lines contains two integers uu and vv (1≤u,v≤n1≤u,v≤n) that mean there's an edge between vertices uu and vv. It's guaranteed that the graph is connected and doesn't contain any self-loops or multiple edges.

Output

If you choose to solve the first problem, then on the first line print 11, followed by a line containing ⌈k2⌉⌈k2⌉ distinct integers not exceeding nn, the vertices in the desired independent set.

If you, however, choose to solve the second problem, then on the first line print 22, followed by a line containing one integer, cc, representing the length of the found cycle, followed by a line containing cc distinct integers not exceeding nn, the vertices in the desired cycle, in the order they appear in the cycle.

Examples

input

Copy

4 4 3
1 2
2 3
3 4
4 1

output

Copy

1
1 3 

input

Copy

4 5 3
1 2
2 3
3 4
4 1
2 4

output

Copy

2
3
2 3 4 

input

Copy

4 6 3
1 2
2 3
3 4
4 1
1 3
2 4

output

Copy

2
3
1 2 3 

input

Copy

5 4 5
1 2
1 3
2 4
2 5

output

Copy

1
1 4 5 

Note

In the first sample:

Notice that printing the independent set {2,4}{2,4} is also OK, but printing the cycle 1−2−3−41−2−3−4 isn't, because its length must be at most 33.

In the second sample:

Notice that printing the independent set {1,3}{1,3} or printing the cycle 2−1−42−1−4 is also OK.

In the third sample:

In the fourth sample:

题目大意:

给你一个N个点(1e5)的无向图,包含M(2e5)条边,你有两种解决方案,一是找到一个独立集包含K / 2向上取整个点,二是找到一个简单环,环内的点数不超过K,输出任意一种方案。

解法:

将图分成树图和非树图两种。

对于树来说,他一定是二分图,可以二分染色一下,取较大的染色集合做独立集(注意点的数量不超过K/2向上取整);

对于非树图,可以先造一棵树,那么剩下的不在树中的边,一定能够使原图形成一个简单环,环的大小就是两端点的路径长度+1,如果构造出的简单环的大小都大于K,只要随便找一个简单环,输出其中的独立集即可。(存在有简单环找不到的情况,这种情况会直接输出独立集)

Accepted code

#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;

#define sc scanf
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define pir pair <int, int>
#define MK(x, y) make_pair(x, y)
#define MEM(x, b) memset(x, b, sizeof(x))
#define MPY(x, b) memcpy(x, b, sizeof(x))
#define lowbit(x) ((x) & -(x))
#define P2(x) ((x) * (x))

typedef long long ll;
const int Mod = 1e9 + 7;
const int N = 2e5 + 100;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
inline ll dpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % Mod; b >>= 1; t = (t*t) % Mod; }return r; }
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t); b >>= 1; t = (t*t); }return r; }

vector <int> G[N << 1], e[N << 1];
vector <int> vec;
int fz[N], n, m, k;
int c[N], c1, c2;
bool ok[N];
pir s[N];

void bfs() {         // 二分染色
	queue <int> q;
	q.push(1), c[1] = 1;
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		if (c[u] == 1)
			c1++;
		else
			c2++;
		for (auto v : G[u]) {
			if (!c[v]) {
				if (c[u] == 1)
					c[v] = 2;
				else
					c[v] = 1;
				q.push(v);
			}
		}
	}
}
void init() {                       // 初始化
	for (int i = 1; i <= n; i++)
		fz[i] = i;
}
int find_(int x) {       // 并查集
	if (x != fz[x])
		x = fz[x] = find_(fz[x]);
	return x;
}

int dep[N], f[N][25];
int d[N];
void dfs(int x, int fa) {        // LCA预处理
	dep[x] = dep[fa] + 1;
	f[x][0] = fa;
	for (int i = 1; (1 << i) <= dep[x]; i++)
		f[x][i] = f[f[x][i - 1]][i - 1];
	for (auto v : e[x]) {
		if (v != fa)
			d[v] = d[x] + 1, dfs(v, x);
	}
} 
int LCA(int x, int y) {              // 查找LCA
	if (dep[x] > dep[y])
		swap(x, y);
	for (int i = 20; i >= 0; i--) {
		if (dep[y] - (1 << i) >= dep[x])
			y = f[y][i];
	}
	if (x == y)
		return x;
	for (int i = 20; i >= 0; i--) {
		if (f[x][i] == f[y][i])
			continue;
		x = f[x][i], y = f[y][i];
	}
	return f[x][0];
}
int dist(int x, int y) {          // 树上距离
	int lca = LCA(x, y);
	return d[x] + d[y] - 2 * d[lca];
}
void route(int x, int y, int op) {   // 打印LCA路径
	if (x == y) {
		if (op == 1)
		    printf("%d ", x);
		else
			vec.push_back(x);
		return;
	}
	if (dep[x] >= dep[y]) {
		if (op == 1)
		    printf("%d ", x);
		else
			vec.push_back(x);
		route(f[x][0], y, op);
	}
	else {
		route(x, f[y][0], op);
		if (op == 1)
	    	printf("%d ", y);
		else
			vec.push_back(y);
	}
}

int main()
{
	cin >> n >> m >> k;
	for (int i = 0; i < m; i++) {
		int u, v;
		sc("%d %d", &u, &v);
		G[u].push_back(v);
		G[v].push_back(u);
		s[i] = { u, v };       // 存边
	}
	if (m == n - 1) {  // 是树
		printf("1\n");
		bfs();          // 二分染色
		int id = c1 > c2 ? 1 : 2;   // 取较大染色集合
		k = (k - 1) / 2 + 1;
		for (int i = 1; i <= n; i++) {
			if (!k)
				break;
			if (c[i] == id)
				printf("%d ", i), k--;
		}
		printf("\n");
		exit(0);
	}

	init();               // 初始化并查集
	int edge = 0;
	for (int i = 0; i < m; i++) { // 建树
		if (edge == n - 1)
			break;
		int u = find_(s[i].first);
		int v = find_(s[i].second);
		if (u != v) {
			fz[u] = v;
			edge++;
			ok[i] = true;   // 标记树上的边
			e[s[i].first].push_back(s[i].second);
			e[s[i].second].push_back(s[i].first);
		}
	}
	dfs(1, 0);               // LCA预处理

	for (int i = 0; i < m; i++) {
		if (ok[i])                     // 只看不在树上的边
			continue;
		int u = s[i].first, v = s[i].second;
		int dis = dist(u, v);
		if (dis + 1 <= k) {               // 简单环的大小
			printf("2\n%d\n", dis + 1);
			route(u, v, 1);             // 打印路径
			printf("\n"), exit(0);
		}
	}
	printf("1\n");
	for (int i = 0; i < m; i++) {
		if (ok[i])
			continue;
		int u = s[i].first, v = s[i].second;
		route(u, v, 2);
		k = (k - 1) / 2 + 1;
		for (int i = 0; i < SZ(vec); i += 2) {    // 隔着取就不相邻了
			if (!k)
				break;
			printf("%d ", vec[i]);
			k--;
		}
		printf("\n"), exit(0);
	}
	return 0;  // 改数组大小!!!用pair记得改宏定义!!!
}

猜你喜欢

转载自blog.csdn.net/weixin_43851525/article/details/106753301
今日推荐