AtCoder Regular Contest 097 - Monochrome Cat

There is a tree with N vertices numbered 1 through N.
The i-th edge connects Vertex x_i and y_i.
Each vertex is painted white or black.
The initial color of Vertex i is represented by a letter c_i.
c_i = W represents the vertex is white; c_i = B represents the vertex is black.
A cat will walk along this tree.
More specifically, she performs one of the following in one second repeatedly:
 - Choose a vertex that is adjacent to the vertex where she is currently, and move to that vertex. Then, invert the color of the destination vertex.
 - Invert the color of the vertex where she is currently.
The cat's objective is to paint all the vertices black. She may start and end performing actions at any vertex.
At least how many seconds does it takes for the cat to achieve her objective?

-----Constraints-----
 - 1 ≤ N ≤ 10^5
 - 1 ≤ x_i,y_i ≤ N (1 ≤ i ≤ N-1)
 - The given graph is a tree.
 - c_i = W or c_i = B.

-----Input-----
Input is given from Standard Input in the following format:
N
x_1 y_1
x_2 y_2
:
x_{N-1} y_{N-1}
c_1c_2..c_N

-----Output-----
Print the minimum number of seconds required to achieve the objective.

-----Sample Input-----
5
1 2
2 3
2 4
4 5
WBBWW

-----Sample Output-----
5

The objective can be achieved in five seconds, for example, as follows:
 - Start at Vertex 1. Change the color of Vertex 1 to black.
 - Move to Vertex 2, then change the color of Vertex 2 to white.
 - Change the color of Vertex 2 to black.
 - Move to Vertex 4, then change the color of Vertex 4 to black.
 - Move to Vertex 5, then change the color of Vertex 5 to black.

Source: atcoder | Difficulty: VERY_HARD


Solutions:
import sys
from collections import deque
input = sys.stdin.readline
N = int(input())
edge = [[] for i in range(N)]
for i in range(N - 1):
	(x, y) = list(map(int, input().split()))
	edge[x - 1].append(y - 1)
	edge[y - 1].append(x - 1)
c = input()[:N]
deg = [len(edge[i]) for i in range(N)]
leaf = set([])
for i in range(N):
	if deg[i] == 1 and c[i] == 'B':
		leaf.add(i)
ban = set([])
while leaf:
	v = leaf.pop()
	ban.add(v)
	deg[v] = 0
	for nv in edge[v]:
		deg[nv] -= 1
		if deg[nv] == 1 and c[nv] == 'B':
			leaf.add(nv)
for i in range(N):
	edge[i] = [nv for nv in edge[i] if nv not in ban]
root = -1
for i in range(N):
	if i not in ban:
		root = i
parent = [-2] * N
deq = deque([(root, -1)])
node = []
while deq:
	(v, pv) = deq.popleft()
	parent[v] = pv
	node.append(v)
	for nv in edge[v]:
		if nv != pv:
			deq.append((nv, v))
node = node[::-1]
for i in range(N):
	edge[i] = [nv for nv in edge[i] if nv != parent[i]]
check = True
for i in range(N):
	check &= deg[i] <= 0
if check:
	print(int(c[root] == 'W'))
	return
cond = [0] * N
for v in range(N):
	if deg[v] % 2 == 1 and c[v] == 'B' or (deg[v] % 2 == 0 and c[v] == 'W'):
		cond[v] += 1
	else:
		cond[v] -= 1
lower = [0] * N
for v in node:
	res = 0
	for nv in edge[v]:
		res = max(res, lower[nv])
	res += 1 + cond[v]
	lower[v] = res
upper = [0] * N
node = node[::-1]
for v in node:
	n = len(edge[v])
	if n > 1:
		left = [0] * n
		right = [0] * n
		for i in range(n - 1):
			nv = edge[v][i]
			left[i] = max(left[i - 1], lower[nv] + 2 + cond[v])
		nv = edge[v][-1]
		upper[nv] = left[n - 2] + cond[nv]
		right[n - 1] = lower[nv] + 2 + cond[v]
		for i in range(n - 2, 0, -1):
			nv = edge[v][i]
			upper[nv] = max(left[i - 1], right[i + 1]) + cond[nv]
			right[i] = max(right[i + 1], lower[nv] + 2 + cond[v])
		if edge[v][0] != pv:
			nv = edge[v][0]
			upper[nv] = right[1] + cond[nv]
	if v != root:
		for nv in edge[v]:
			upper[nv] = max(upper[nv], upper[v] + 1 + cond[nv])
base = sum((deg[i] for i in range(N))) + sum((cond[i] == 1 for i in range(N)))
print(base - max(max(upper), max(lower)))


import sys
sys.setrecursionlimit(200000)
from collections import deque
MM = 0

def dfs(v, p):
	global MM
	if v != p and linknum[v] == 1:
		return 0
	cl = []
	for nex in lis[v]:
		if nex != p and exist[nex]:
			tmp = dfs(nex, v)
			cl.append(tmp)
	cl.sort()
	cl.reverse()
	if len(cl) == 1:
		MM = max(MM, cl[0])
	elif (linknum[v] + c[v]) % 2 == 1:
		MM = max(MM, cl[0] + cl[1] + 2)
	else:
		MM = max(MM, cl[0] + cl[1])
	if (linknum[v] + c[v]) % 2 == 1:
		return cl[0] + 2
	else:
		return cl[0]
N = int(input())
lis = [[] for i in range(N)]
linknum = [0] * N
for i in range(N - 1):
	(u, v) = map(int, input().split())
	u -= 1
	v -= 1
	lis[u].append(v)
	lis[v].append(u)
	linknum[u] += 1
	linknum[v] += 1
ctmp = input()
c = []
for i in ctmp:
	if i == 'B':
		c.append(0)
	else:
		c.append(1)
exist = [True] * N
q = deque([])
for i in range(N):
	if linknum[i] <= 1 and c[i] == 0:
		q.append(i)
while len(q) > 0:
	now = q.popleft()
	exist[now] = False
	linknum[now] = 0
	for nex in lis[now]:
		linknum[nex] -= 1
		if linknum[nex] == 1 and c[nex] == 0:
			q.append(nex)
start = None
for i in range(N):
	if exist[i]:
		start = i
		break
else:
	print(0)
	sys.exit()
if linknum[start] == 0:
	print(1)
	sys.exit()
ans = 0
for i in range(N):
	if exist[i]:
		if (linknum[i] + c[i]) % 2 == 0:
			ans += linknum[i]
		else:
			ans += linknum[i] + 1
MM = 0
pick = dfs(start, start)
print(ans - MM)


from collections import deque

def first_cut(links, colors):
	tmp_links = links.copy()
	for (v, neighbors) in tmp_links.items():
		while len(neighbors) == 1 and colors[v]:
			del links[v]
			par = neighbors.pop()
			links[par].remove(v)
			v = par
			neighbors = links[par]
	return links

def diameter(links, flags):

	def dfs(s):
		fs = flags[s]
		(d, v) = (0, 0)
		q = deque(sorted(((fs + flags[v], v, s) for v in links[s])))
		while q:
			(d, v, a) = q.popleft()
			for u in links[v]:
				if u == a:
					continue
				fu = flags[u]
				if fu:
					q.append((d + 1, u, v))
				else:
					q.appendleft((d, u, v))
		return (d, v)
	s = next(iter(links))
	(_, t) = dfs(s)
	(d, _) = dfs(t)
	return d

def solve(links, colors):
	if all(colors):
		return 0
	links = first_cut(links, colors)
	k = len(links)
	if k == 1:
		return 1
	flags = {v: colors[v] ^ (len(link) % 2 == 0) for (v, link) in links.items()}
	euler_tour = 2 * (k - 1) + sum(flags.values())
	return euler_tour - 2 * diameter(links, flags)
n = int(input())
links = {i: set() for i in range(n)}
for _ in range(n - 1):
	(x, y) = map(int, input().split())
	x -= 1
	y -= 1
	links[x].add(y)
	links[y].add(x)
colors = [c == 'B' for c in input()]
print(solve(links, colors))


import sys
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
readlines = sys.stdin.buffer.readlines
N = int(readline())
data = read().split()
m = map(int, data[:-1])
XY = zip(m, m)
C = [0] + [1 * (x == ord('W')) for x in data[-1]]
graph = [set() for _ in range(N + 1)]
for (x, y) in XY:
	graph[x].add(y)
	graph[y].add(x)
deg = [len(x) for x in graph]
b_leaf = [i for i in range(N + 1) if deg[i] == 1 and (not C[i])]
while b_leaf:
	x = b_leaf.pop()
	for y in graph[x]:
		graph[y].remove(x)
		deg[y] -= 1
		if deg[y] == 1 and (not C[y]):
			b_leaf.append(y)
	deg[x] = 0
	graph[x].clear()
V = set((i for (i, E) in enumerate(graph) if E))
if len(V) <= 2:
	print(sum(C))
	exit()
root = None
for v in V:
	if deg[v] > 1:
		root = v
		break
parent = [0] * (N + 1)
order = []
stack = [root]
while stack:
	x = stack.pop()
	order.append(x)
	for y in graph[x]:
		if y == parent[x]:
			continue
		parent[y] = x
		stack.append(y)
for i in range(N + 1):
	C[i] ^= 1 & deg[i]
cost_full = sum(deg) + sum(C)
opt = 0
dp = [0] * (N + 1)
for v in order[::-1]:
	if deg[v] == 1:
		continue
	arr = sorted((dp[w] for w in graph[v] if w != parent[v]), reverse=True) + [0]
	x = arr[0] + arr[1] + C[v]
	if opt < x:
		opt = x
	dp[v] = arr[0] + C[v]
answer = cost_full - 2 * opt
print(answer)