Problem:
Score : 800 points

Problem Statement

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 1

5
1 2
2 3
2 4
4 5
WBBWW

Sample Output 1

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.

Sample Input 2

6
3 1
4 5
2 6
6 1
3 4
WWBWBB

Sample Output 2

7

Sample Input 3

1
B

Sample Output 3

0

Sample Input 4

20
2 19
5 13
6 4
15 6
12 19
13 19
3 11
8 3
3 20
16 13
7 14
3 17
7 8
10 20
11 9
8 18
8 2
10 1
6 13
WBWBWBBWWWBBWWBBBBBW

Sample Output 4

21

Solution in C# (.NET Core 3.1.201) (judge verdict: Accepted)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Linq;

using E = System.Linq.Enumerable;
using System.Threading;

internal partial class Solver {
    public void Run() {
        var n = ni();
        var adj = Enumerable.Range(0, n).Select(_ => new List<int>()).ToArray();
        for (int i = 0; i < n - 1; i++) {
            var x = ni() - 1;
            var y = ni() - 1;
            adj[x].Add(y);
            adj[y].Add(x);
        }
        var s = ns();

        var wnum = s.Count(c => c == 'W');
        if (wnum == 0) {
            cout.WriteLine(0);
        } else if (wnum == 1) {
            cout.WriteLine(1);
        } else {
            var queue = new Queue<int>();
            for (int i = 0; i < n; i++) {
                if (adj[i].Count == 1) queue.Enqueue(i);
            }
            while (queue.Count > 0) {
                var now = queue.Dequeue();
                if (s[now] == 'B' && adj[now].Count == 1) {
                    var next = adj[now][0];
                    if (s[next] == 'B') {
                        queue.Enqueue(next);
                    }
                    adj[now].Clear();
                    if (!adj[next].Remove(now)) throw new Exception();
                }
            }
            var isBlack = new bool[n];
            for (int i = 0; i < n; i++) {
                var degree = adj[i].Count;
                if (degree == 1 && s[i] == 'B') throw new Exception();
                if (degree % 2 == 1) isBlack[i] = (s[i] == 'W');
                else isBlack[i] = (s[i] == 'B');
            }
            int degreeSum = 0, root = -1;
            var reRooting = new ReRooting<int>((a, b) => {
                return Math.Max(a, b);
            }, (v, index) => {
                return (!isBlack[index] ? 1 : 0) + v;
            }, 0);
            for (int i = 0; i < n; i++) {
                if (adj[i].Count > 0) {
                    degreeSum += adj[i].Count;
                    root = i;
                    if (!isBlack[i]) degreeSum++;
                }
            }

            var res = reRooting.Solve(adj);
            var ans = degreeSum - 2 * E.Range(0, n).Select(i => res[i]).Max();

            cout.WriteLine(ans);
        }
    }

}

// PREWRITEN CODE BEGINS FROM HERE

static public class StringExtensions {
    static public string JoinToString<T>(this IEnumerable<T> source, string separator = " ") {
        return string.Join(separator, source);
    }
}

public class ReRooting<TMonoid> {
    private readonly TMonoid _identity;
    private readonly Func<TMonoid, TMonoid, TMonoid> _merge;
    private readonly Func<TMonoid, int, TMonoid> _appendNode;

    public ReRooting(Func<TMonoid, TMonoid, TMonoid> merge,
        Func<TMonoid, int, TMonoid> appendNode,
        TMonoid identity) {
        _merge = merge;
        _appendNode = appendNode;
        _identity = identity;
    }

    public TMonoid[] Solve(List<int>[] adj) {
        var n = adj.Length;
        var ans = new TMonoid[n];
        var parents = new int[n];
        var visited = new bool[n];
        var stack = new Stack<int>();
        var dp = new TMonoid[n];
        var fromParent = new TMonoid[n];
        var accumulateFromTail = new TMonoid[n + 1];
        var subTreeResult = new TMonoid[n];
        for (int root = 0; root < n; root++) {
            if (visited[root]) continue;
            stack.Push(root);
            parents[root] = -1;
            var permutation = new List<int>();
            while (stack.Count > 0) {
                var current = stack.Pop();
                if (visited[current]) continue;
                visited[current] = true;
                permutation.Add(current);
                foreach (var child in adj[current]) {
                    if (!visited[child]) {
                        parents[child] = current;
                        stack.Push(child);
                    }
                }
            }
            foreach (var p in permutation.AsEnumerable().Reverse()) {
                var val = _identity;
                foreach (var child in adj[p]) {
                    if (child == parents[p]) continue;
                    val = _merge(val, dp[child]);
                }
                dp[p] = _appendNode(val, p);
            }
            foreach (var p in permutation) {
                int count = adj[p].Count;
                var accumulate = _identity;
                if (count > 0) {
                    for (int i = 0; i < count; i++) {
                        var child = adj[p][i];
                        subTreeResult[i] = child == parents[p] ? fromParent[p] : dp[child];
                    }
                    accumulateFromTail[count] = _identity;
                    for (int i = count - 1; i >= 0; i--) {
                        accumulateFromTail[i] = _merge(subTreeResult[i], accumulateFromTail[i + 1]);
                    }
                    for (int i = 0; i < count; i++) {
                        var child = adj[p][i];
                        if (child != parents[p]) {
                            fromParent[child] = _appendNode(_merge(accumulate, accumulateFromTail[i + 1]), p);
                        }
                        accumulate = _merge(accumulate, subTreeResult[i]);
                    }
                }
                ans[p] = _appendNode(accumulate, p);
            }
        }
        return ans;
    }
}
internal partial class Solver : Scanner {
    static readonly int? StackSizeInMebiByte = null; //50;
    public static void StartAndJoin(Action action, int maxStackSize) {
        var thread = new Thread(new ThreadStart(action), maxStackSize);
        thread.Start();
        thread.Join();
    }

    public static void Main() {
#if LOCAL
        byte[] inputBuffer = new byte[1000000];
        var inputStream = Console.OpenStandardInput(inputBuffer.Length);
        using (var reader = new StreamReader(inputStream, Console.InputEncoding, false, inputBuffer.Length)) {
            Console.SetIn(reader);
            new Solver(Console.In, Console.Out).Run();
        }
#else
        Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false });
        if (StackSizeInMebiByte.HasValue) {
            StartAndJoin(() => new Solver(Console.In, Console.Out).Run(), StackSizeInMebiByte.Value * 1024 * 1024);
        } else {
            new Solver(Console.In, Console.Out).Run();
        }
        Console.Out.Flush();
#endif
    }

#pragma warning disable IDE0052
    private readonly TextReader cin;
    private readonly TextWriter cout;
    private readonly TextWriter cerr;
#pragma warning restore IDE0052

    public Solver(TextReader reader, TextWriter writer)
        : base(reader) {
        cin = reader;
        cout = writer;
        cerr = Console.Error;
    }

    public Solver(string input, TextWriter writer)
        : this(new StringReader(input), writer) {
    }

#pragma warning disable IDE1006
#pragma warning disable IDE0051
    private int ni() { return NextInt(); }
    private int[] ni(int n) { return NextIntArray(n); }
    private long nl() { return NextLong(); }
    private long[] nl(int n) { return NextLongArray(n); }
    private double nd() { return NextDouble(); }
    private double[] nd(int n) { return NextDoubleArray(n); }
    private string ns() { return Next(); }
    private string[] ns(int n) { return NextArray(n); }
#pragma warning restore IDE1006
#pragma warning restore IDE0051
}

#if DEBUG
internal static class LinqPadExtension {
    public static string TextDump<T>(this T obj) {
        if (obj is IEnumerable) return (obj as IEnumerable).Cast<object>().JoinToString().Dump();
        else return obj.ToString().Dump();
    }
    public static T Dump<T>(this T obj) {
        return LINQPad.Extensions.Dump(obj);
    }
}
#endif

public class Scanner {
    private readonly TextReader Reader;
    private readonly CultureInfo ci = CultureInfo.InvariantCulture;

    private readonly char[] buffer = new char[2 * 1024];
    private int cursor = 0, length = 0;
    private string Token;
    private readonly StringBuilder sb = new StringBuilder(1024);

    public Scanner()
        : this(Console.In) {
    }

    public Scanner(TextReader reader) {
        Reader = reader;
    }

    public int NextInt() { return checked((int)NextLong()); }
    public long NextLong() {
        var s = Next();
        long r = 0;
        int i = 0;
        bool negative = false;
        if (s[i] == '-') {
            negative = true;
            i++;
        }
        for (; i < s.Length; i++) {
            r = r * 10 + (s[i] - '0');
#if DEBUG
            if (!char.IsDigit(s[i])) throw new FormatException();
#endif
        }
        return negative ? -r : r;
    }
    public double NextDouble() { return double.Parse(Next(), ci); }
    public string[] NextArray(int size) {
        string[] array = new string[size];
        for (int i = 0; i < size; i++) {
            array[i] = Next();
        }

        return array;
    }
    public int[] NextIntArray(int size) {
        int[] array = new int[size];
        for (int i = 0; i < size; i++) {
            array[i] = NextInt();
        }

        return array;
    }

    public long[] NextLongArray(int size) {
        long[] array = new long[size];
        for (int i = 0; i < size; i++) {
            array[i] = NextLong();
        }

        return array;
    }

    public double[] NextDoubleArray(int size) {
        double[] array = new double[size];
        for (int i = 0; i < size; i++) {
            array[i] = NextDouble();
        }

        return array;
    }

    public string Next() {
        if (Token == null) {
            if (!StockToken()) {
                throw new Exception();
            }
        }
        var token = Token;
        Token = null;
        return token;
    }

    public bool HasNext() {
        if (Token != null) {
            return true;
        }

        return StockToken();
    }

    private bool StockToken() {
        while (true) {
            sb.Clear();
            while (true) {
                if (cursor >= length) {
                    cursor = 0;
                    if ((length = Reader.Read(buffer, 0, buffer.Length)) <= 0) {
                        break;
                    }
                }
                var c = buffer[cursor++];
                if (33 <= c && c <= 126) {
                    sb.Append(c);
                } else {
                    if (sb.Length > 0) break;
                }
            }

            if (sb.Length > 0) {
                Token = sb.ToString();
                return true;
            }

            return false;
        }
    }
}