// Alex Coleman
// 2/21/2017
// Solution to 2017 FHSPS Playoff Questions: Dragons

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Scanner;

public class dragons_coleman {
	
	public static void main(String[] args) {
		
		Scanner in = new Scanner(System.in);
		int T = in.nextInt();
		
		// Process all cases.
		while(T --> 0) {
			
			// Get input.
			int n = in.nextInt();
			HashMap<String,Integer> map = new HashMap<>();
			int ID = 0;
			
			// Set up Dinic's object.
			Dinic d = new Dinic(2*n);
			
			// Read in edges.
			for(int i=0;i<n;i++) {
				String a = in.next(), p = in.next() , w = in.next();
				
				// Put in new vertex, if necessary.
				if(!map.containsKey(p)) {
					d.add(d.s, ID, 1, 0);
					map.put(p, ID++);
				}
				
				// Same here.
				if(!map.containsKey(w)) {
					d.add(ID, d.t, 1, 0);
					map.put(w, ID++);
				}
				
				// Add edge to graph with capacity 1.
				int id1 = map.get(p);
				int id2 = map.get(w);
				d.add(id1, id2, 1, 0);
			}
			
			// Run network flow and that's our answer.
			int flow = d.flow();
			System.out.println(flow);
		}
	}
	
	// Code for Dinic's Network Flow Algorithm
	static class Dinic {
		
		// An edge connects v1 to v2 with a capacity of cap, flow of flow.
		static class Edge {
			int v1, v2, cap, flow;
			Edge rev;
			Edge(int V1, int V2, int Cap, int Flow) {
				v1 = V1; v2 = V2; cap = Cap; flow = Flow;
			}
		}
		
		// Queue for the top level BFS.
		ArrayDeque<Integer> q;
		
		// Stores the graph.
		ArrayList<Edge>[] adj;
		int n, s, t, oo = (int)1E9;
		boolean[] blocked;
		int[] dist;

		// Constructor.
		public Dinic (int N) {
			
			// s is the source, t is the sink, add these as last two nodes.
			n = N; s = n++; t = n++;
			
			// Everything else is empty.
			blocked = new boolean[n];
			dist = new int[n];
			q = new ArrayDeque<Integer>();
			adj = new ArrayList[n];
			for(int i = 0; i < n; ++i)
				adj[i] = new ArrayList<Edge>();
		}

		// Just adds an edge and ALSO adds it going backwards.
		void add(int v1, int v2, int cap, int flow) {
			Edge e = new Edge(v1, v2, cap, flow);
			Edge rev = new Edge(v2, v1, 0, 0);
			adj[v1].add(rev.rev = e);
			adj[v2].add(e.rev = rev);
		}

		// Runs other level BFS.
		boolean bfs() {
			
			// Set up BFS
			q.clear();
			Arrays.fill(dist, -1);
			dist[t] = 0;
			q.add(t);
			
			// Go backwards from sink looking for source.
			// We just care to mark distances left to the sink.
			while(!q.isEmpty()) {
				int node = q.poll();
				if(node == s) 
					return true;
				for(Edge e : adj[node]) {
					if(e.rev.cap > e.rev.flow && dist[e.v2] == -1) {
						dist[e.v2] = dist[node] + 1;
						q.add(e.v2);
					}
				}
			}
			
			// Augmenting paths exist iff we made it back to the source.
			return dist[s] != -1;
		}
		
		// Runs inner DFS in Dinic's, from node pos with a flow of min.
		int dfs(int pos, int min) {
			
			// Made it to the sink, we're good, return this as our max flow for the augmenting path.
			if(pos == t) 
				return min;
			int flow = 0;
			
			// Try each edge from here.
			for(Edge e : adj[pos]) {
				int cur = 0;
				
				// If our destination isn't blocked and it's 1 closer to the sink and there's flow, we 
				// can go this way.
				if(!blocked[e.v2] && dist[e.v2] == dist[pos]-1 && e.cap - e.flow > 0) {
					
					// Recursively run dfs from here - limiting flow based on current and what's left on this edge.
					cur = dfs(e.v2, Math.min(min-flow, e.cap - e.flow));
					
					// Add the flow through this edge and subtract it from the reverse flow.
					e.flow += cur;
					e.rev.flow = -e.flow;
					
					// Add to the total flow.
					flow += cur;
				}
				
				// No more can go through, we're good.
				if(flow == min)
					return flow;
			}
			
			// mark if this node is now blocked.
			blocked[pos] = flow != min;
			
			// This is the flow
			return flow;
		}
		int flow() {
			clear();
			int ret = 0;
			
			// Run a top level BFS.
			while(bfs()) {
				
				// Reset this.
				Arrays.fill(blocked, false);
				
				// Run multiple DFS's until there is no flow left to push through.
				ret += dfs(s, oo);
			}
			return ret;
		}
		
		// Just resets flow through all edges to be 0.
		void clear() {
			for(ArrayList<Edge> edges : adj)
				for(Edge e : edges)
					e.flow = 0;
		}
	}
}
