Source Code
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) {
        InputReader in = new InputReader(System.in);
        PrintWriter out = new PrintWriter(System.out);
        int testCases = in.nextInt();
        for (int i = 0; i < testCases; ++i) {
            Solver solver = new Solver();
            solver.solve(in, out);
        }
        out.close();
    }

    static class Solver {
        int n;
        List<List<Edge>> adj;
        boolean[] down;
        boolean[][] visDir;
        boolean[] up;
        boolean[] can;

        public void solve(InputReader in, PrintWriter out) {
            n = in.nextInt();
            adj = new ArrayList<>();
            for (int i = 0; i < n; ++i) {
                adj.add(new ArrayList<>());
            }
            for (int i = 0; i < n - 1; ++i) {
                int a = in.nextInt() - 1;
                int b = in.nextInt() - 1;
                int c = mapDirToInt(in.next().charAt(0));
                
                adj.get(a).add(new Edge(b, c));
                adj.get(b).add(new Edge(a, c));
            }
            int root = 0;
            down = new boolean[n];
            visDir = new boolean[n][4];
            dfsDown(0, -1);
            up = new boolean[n];
            up[root] = true;
            dfsUp(0, -1);
            can = new boolean[n];
            for (int node = 0; node < n; ++node) {
                int[] dirFreq = new int[4];
                for (Edge e : adj.get(node)) {
                    ++dirFreq[e.getDir()];
                }
                can[node] = down[node] && up[node];
                for (int d = 0; d < 4; ++d) {
                    if (dirFreq[d] > 1) {
                        can[node] = false;
                        break;
                    }
                }
            }
            for (int node = 0; node < n; ++node) {
                if (can[node]) {
                    out.printf("%d ", node + 1);;
                }
            }
            out.println();
        }

        void dfsDown(int node, int parent) {
            down[node] = true;
            for (Edge e : adj.get(node)) {
                int child = e.getDestination();
                if (child == parent) {
                    continue;
                }
                int dir = e.getDir();
                if (visDir[node][dir]) {
                    down[node] = false;
                } else {
                    visDir[node][dir] = true;
                }
                dfsDown(child, node);
                down[node] = down[node] && down[child] && !visDir[child][dir ^ 1];
            }
        }

        void dfsUp(int node, int parent) {
            int[] dirFreq = new int[4];
            int cntDown = 0;
            int cntAll = 0;
            for (Edge e : adj.get(node)) {
                int child = e.getDestination();
                int dir = e.getDir();
                if (child != parent) {
                    ++cntAll;
                    if (down[child] && !visDir[child][dir ^ 1]) {
                        ++cntDown;
                    }
                }
                ++dirFreq[dir];
            }
            for (Edge e : adj.get(node)) {
                int child = e.getDestination();
                if (child == parent) {
                    continue;
                }
                int dir = e.getDir();
                --dirFreq[dir];
                if (down[child] && !visDir[child][dir ^ 1]) {
                    --cntDown;
                }
                boolean hasDuplicates = false;
                for (int d = 0; d < 4; ++d) {
                    if (dirFreq[d] > 1) {
                        hasDuplicates = true;
                        break;
                    }
                }
                up[child] = up[node] && !hasDuplicates && (cntDown == cntAll - 1) && dirFreq[dir ^ 1] == 0;
                dfsUp(child, node);
                if (down[child] && !visDir[child][dir ^ 1]) {
                    ++cntDown;
                }
                ++dirFreq[dir];
            }
        }

        int mapDirToInt(char dir) {
            if (dir == 'L') return 0;
            if (dir == 'R') return 1;
            if (dir == 'U') return 2;
            if (dir == 'D') return 3;
            return -1;
        }

        boolean areOpposite(int d1, int d2) {
            return (d1 ^ d2) == 1;
        }
        
        static class Edge {
            private int destination;
            private int dir;
            
            public Edge(int to, int dir) {
                this.destination = to;
                this.dir = dir;
            }

            public int getDestination() {
                return destination;
            }

            public int getDir() {
                return dir;
            }
        };
    };

    static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }
    }
}
Copy
Flow Sources I abdulrahman_aj
Java 11
2028 ms
112.3 MB
Time Limit Exceeded