Kefaa has developed a novel decomposition of a tree. He claims that this decomposition solves many difficult problems related to trees. However, he doesn't know how to find it quickly, so he asks you to help him.
You are given a tree with $N$ vertices numbered $1$ through $N$. Let's denote an edge between vertices $u$ and $v$ by $(u, v)$. The triple-tree decomposition is a partition of edges of the tree into unordered triples of edges $(a, b), (a, c), (a, d)$ such that $a \neq b \neq c \neq d$. Each edge must belong to exactly one triple.
Help Kefaa with this problem — find a triple-tree decomposition of the given tree or determine that no such decomposition exists.

-----Input-----
- The first line of the input contains a single integer $T$ denoting the number of test cases. The description of $T$ test cases follows.
- The first line of each test case contains a single integer $N$.
- Each of the following $N-1$ lines contains two space-separated integers $u$ and $v$ describing an edge between vertices $u$ and $v$ of the tree.

-----Output-----
- For each test case, print a line containing the string "YES" if a triple-tree decomposition of the given tree exists or "NO" otherwise.
- If it exists, print $\frac{N-1}{3}$ more lines describing a decomposition.
- Each of these lines should contain four space-separated integers $a$, $b$, $c$ and $d$ describing a triple of edges $(a, b), (a, c), (a, d)$.
If more than one triple-tree decomposition exists, you may output any one.

-----Constraints-----
- $1 \le T \le 100$
- $2 \le N \le 2 \cdot 10^5$
- $1 \le u, v \le N$
- the sum of $N$ over all test cases does not exceed $2 \cdot 10^5$

-----Subtasks-----
Subtask #1 (20 points): $2 \le N \le 10$
Subtask #2 (30 points):$2 \le N \le 5000$ and the sum of $N$ overall testcases doesn't exceed $5000$
Subtask #3 (50 points): original constraints

-----Example Input-----
2
4
1 2
1 3
1 4
7
1 2
2 3
1 4
4 5
1 6
6 7

-----Example Output-----
YES
1 2 3 4
NO

Source: codechef | Difficulty: VERY_HARD
Tags: Graph algorithms

Graph traversal


Solutions:
def dfs(u, p):
	l = [u]
	for v in g[u]:
		if v != p:
			r = dfs(v, u)
			if r == 2:
				return 2
			if r == 1:
				l.append(v)
				if len(l) == 4:
					out.append(l)
					l = [u]
	if len(l) == 3:
		out.append(l + [p])
	return len(l)
t = int(input())
for _ in range(t):
	n = int(input())
	g = [[] for _ in range(n + 1)]
	for _ in range(n - 1):
		(u, v) = map(int, input().split())
		g[u].append(v)
		g[v].append(u)
	out = []
	if dfs(1, 0) == 1:
		print('YES')
		for l in out:
			print(*l)
	else:
		print('NO')


for t in range(int(input())):
	N = int(input())
	G = [set() for i in range(N + 1)]
	for i in range(N - 1):
		(u, v) = map(int, input().split())
		G[u].add(v)
		G[v].add(u)
	leaves = [u for u in range(1, N + 1) if len(G[u]) == 1]
	cur = [set() for i in range(N + 1)]
	for leaf in leaves:
		p = G[leaf].pop()
		cur[p].add(leaf)
	(cnt2, cnt3) = (set(), set())
	for u in range(1, N + 1):
		if len(cur[u]) >= 3:
			cnt3.add(u)
		elif len(cur[u]) == 2 and len(G[u]) == 3:
			cnt2.add(u)
	ans = []
	while cnt3 or cnt2:
		while cnt3:
			u = cnt3.pop()
			(leaf1, leaf2, leaf3) = (cur[u].pop(), cur[u].pop(), cur[u].pop())
			ans.append((u, leaf1, leaf2, leaf3))
			G[u].remove(leaf1)
			G[u].remove(leaf2)
			G[u].remove(leaf3)
			if len(cur[u]) >= 3:
				cnt3.add(u)
			elif len(cur[u]) == 2 and len(G[u]) == 3:
				cnt2.add(u)
			if len(G[u]) == 1:
				p = G[u].pop()
				cnt2.discard(p)
				cnt3.discard(p)
				cur[p].add(u)
				if len(cur[p]) >= 3:
					cnt3.add(p)
				elif len(cur[p]) == 2 and len(G[p]) == 3:
					cnt2.add(p)
		while cnt2:
			u = cnt2.pop()
			(leaf1, leaf2) = (cur[u].pop(), cur[u].pop())
			G[u].remove(leaf1)
			G[u].remove(leaf2)
			leaf3 = G[u].pop()
			ans.append((u, leaf1, leaf2, leaf3))
			(u, leaf3) = (leaf3, u)
			G[u].remove(leaf3)
			if len(G[u]) == 1:
				p = G[u].pop()
				cnt2.discard(p)
				cnt3.discard(p)
				cur[p].add(u)
				if len(cur[p]) >= 3:
					cnt3.add(p)
				elif len(cur[p]) == 2 and len(G[p]) == 3:
					cnt2.add(p)
	if len(ans) * 3 == N - 1:
		print('YES')
		for triple in ans:
			print(*triple)
	else:
		print('NO')


def dfs(u, p):
	l = s = str(u)
	k = 0
	for v in g[u]:
		if v != p:
			r = dfs(v, u)
			if r == 1:
				return 1
			if r == 0:
				l += format(v, ' ')
				k += 1
				if k == 3:
					out.append(l)
					(l, k) = (s, 0)
	if k == 2:
		out.append(l + format(p, ' '))
	return k
t = int(input())
for _ in range(t):
	n = int(input())
	g = [[] for _ in range(n + 1)]
	for _ in range(n - 1):
		(u, v) = map(int, input().split())
		g[u].append(v)
		g[v].append(u)
	out = []
	if dfs(1, 0) == 0:
		print('YES')
		for l in out:
			print(l)
	else:
		print('NO')


test = int(input())
for t in range(test):
	n = int(input())
	adj = [[] for i in range(n + 1)]
	for _ in range(n - 1):
		(a, b) = list(map(int, input().split()))
		adj[a].append(b)
		adj[b].append(a)
	root = 1
	(q, s) = ([root], set([root]))
	for x in q:
		adj[x] = [p for p in adj[x] if p not in s]
		q.extend(adj[x])
		s.update(adj[x])
	ans = True
	if n < 4:
		ans = False
	for i in range(n + 1):
		if len(adj[i]) % 3 != 0:
			ans = False
	if ans:
		print('YES')
		for i in range(n + 1):
			while len(adj[i]):
				print(i, adj[i][0], adj[i][1], adj[i][2])
				adj[i].pop(0)
				adj[i].pop(0)
				adj[i].pop(0)
	else:
		print('NO')