import std.algorithm, std.array, std.conv, std.stdio, std.typecons;

alias Edge = Tuple!(uint, uint);
alias EdgeSet = bool[Edge];
alias Face = Tuple!(uint, uint, uint);
alias FaceSet = bool[Face];
alias ASC = Tuple!(EdgeSet, "edges", FaceSet, "faces");

ASC create_normalized_asc(uint[][] face_list, ref uint N) {
	// normalize
	bool[uint] verts;
	foreach (face; face_list) {
		verts[face[0]] = true;
		verts[face[1]] = true;
		verts[face[2]] = true;
	}
	uint[uint] nv;
	uint N2;
	foreach (i; 1 .. N+1) {
		if (i in verts) nv[i] = N2++;
	}
	N = N2;
	
	EdgeSet edges;
	FaceSet faces;
	foreach (face; face_list) {
		edges[Edge(nv[face[0]], nv[face[1]])] = true;
		edges[Edge(nv[face[0]], nv[face[2]])] = true;
		edges[Edge(nv[face[1]], nv[face[2]])] = true;
		faces[Face(nv[face[0]], nv[face[1]], nv[face[2]])] = true;
	}
	
	return ASC(edges, faces);
}

// check if the ASC asc is connected
bool is_connected(ASC asc, uint N) {
	bool[uint] vertex_set;
	auto uf = new UnionFind(N);
	foreach (face; asc.faces.keys) {
		uf.unite(face[0], face[1]);
		uf.unite(face[0], face[2]);
		vertex_set[face[0]] = vertex_set[face[1]] = vertex_set[face[2]] = true;
	}
	foreach (edge; asc.edges.keys) {
		uf.unite(edge[0], edge[1]);
		vertex_set[edge[0]] = vertex_set[edge[1]] = true;
	}
	return uf.size(vertex_set.keys[0]) == vertex_set.length;
}

// calculate link {i} for each i
ASC[] links(ASC asc, uint N) {
	auto result = new ASC[N];
	foreach (face; asc.faces.keys) {
		result[face[0]].edges[Edge(face[1], face[2])] = true;
		result[face[1]].edges[Edge(face[0], face[2])] = true;
		result[face[2]].edges[Edge(face[0], face[1])] = true;
	}
	return result;
}

uint H1_homology_rank(ASC asc, uint N) {
	// dim im d_2
	F_2Vector[] d_2_matrix;
	auto edge_list = asc.edges.keys;
	foreach (face; asc.faces.keys) {
		F_2Vector vec;
		vec.set_entry(cast(uint) edge_list.countUntil(Edge(face[0], face[1])), 1);
		vec.set_entry(cast(uint) edge_list.countUntil(Edge(face[0], face[2])), 1);
		vec.set_entry(cast(uint) edge_list.countUntil(Edge(face[1], face[2])), 1);
		d_2_matrix ~= vec;
	}
	auto b_rank = get_rank(d_2_matrix);
	
	F_2Vector[] d_1_matrix;
	foreach (edge; asc.edges.keys) {
		F_2Vector vec;
		vec.set_entry(edge[0], 1);
		vec.set_entry(edge[1], 1);
		d_1_matrix ~= vec;
	}
	auto z_rank = cast(uint)asc.edges.length - get_rank(d_1_matrix);
	
	assert(z_rank >= b_rank);
	return z_rank - b_rank;
}

void main() {
	auto MN = readln.split.to!(uint[]);
	auto N = MN[0], M = MN[1];
	uint[][] face_list;
	foreach (m; 0 .. M) {
		face_list ~= readln.split.to!(uint[]);
	}
	auto asc = create_normalized_asc(face_list, N);
	
	auto co  = is_connected(asc, N);
	auto dim = H1_homology_rank(asc, N);
	auto lk  = links(asc, N).map!(a => is_connected(a, N)).all!(a => a);
	
	if (!co)           writeln("CO");
	else if (dim != 0) writeln("H1");
	else if (!lk)      writeln("LK");
	else               writeln("CM");
}

/***************************************************************************
****************************************************************************/

alias F_2Vector = ulong[39];		// 39 * 8 > 300
static ulong_size = 8 * (ulong.sizeof);

void set_entry(ref F_2Vector vector, uint n_th, int val) {
	if (val)
		vector[n_th / ulong_size] |= 1UL << n_th % ulong_size;
	else
		vector[n_th / ulong_size] &= ~0UL - (1UL << (n_th % ulong_size));
}

bool is_zero(F_2Vector vector) {
	foreach (e; vector) if (e != 0) return false;
	return true;
}

F_2Vector slide_vector(F_2Vector vector) {
	F_2Vector slided;
	foreach (i; 0 .. vector.length - 1) {
		slided[i] = vector[i+1];
	}
	return slided;
}

// by sweep
// vectors can change
uint get_rank(F_2Vector[] vectors ...) {
	if (vectors.all!(v => is_zero(v))) return 0;
	
	// make the left-most vector nonzero
	while (vectors.all!(v => v[0] == 0))
		vectors.each!((ref v) => v = slide_vector(v));
	while (vectors.all!(v => v[0] % 2 == 0))
		vectors.each!((ref v) => v[0] >>= 1);
	foreach (ref v; vectors) {
		if (v[0] % 2 != 0) {
			auto tmp = vectors[0];
			vectors[0] = v;
			v = tmp;
		}
	}
	
	// sweeping
	foreach (ref v; vectors[1 .. $]) {
		if (v[0] % 2 != 0)
			v[] ^= vectors[0][];
	}
	
	vectors[1 .. $].map!((ref v) => v[0] >>= 1);
	return 1 + get_rank(vectors[1 .. $]);
}

// Union-tree of {0, 1, ..., num-1}
class UnionFind {
	size_t num;
	private size_t[] parent;
	private size_t[] rank;
	private size_t[] size_;
	
	this (size_t num) {
	    this.num = num;
	    parent.length = num;
	    foreach (n; 0 .. num) parent[n] = n;
	    rank.length = num;
	    size_.length = num;
	}
	
	size_t root(size_t n) {
	    assert(n < num);
	    if (parent[n] == n) return n;
	    else return parent[n] = root(parent[n]);    // recursively set the root
	}
	
	bool is_same(size_t n, size_t m) {
		assert(n < num && m < num);
		return root(n) == root(m);
	}
	
	// false if already united
	bool unite(size_t n, size_t m) {
		assert(n < num && m < num);
		auto rn = root(n), rm = root(m);
		if (rn == rm) return false;
		// swap if necessary to force rank[rm] <= rank[rn]
		if (rank[rn] < rank[rm]) {
			auto tmp = rm;
			rm = rn;
			rn = tmp;
		}
		parent[rm] = rn;
		if (rank[rn] == rank[rm]) ++rank[rn];
		size_[rn] += size_[rm] + 1;
		return true;
	}
	
	size_t size(size_t n) {
		return size_[root(n)]+1;
	}
	
	size_t size() {
		size_t result;
		foreach (i; 0 .. num)
			if (root(i) == i) result += 1;
		return result;
	}
}