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