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記得改宏定義!!!
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章