The "BerCorp" company has got n employees. These employees can use m approved official languages for the formal correspondence. The languages are numbered with integers from 1 to m. For each employee we have the list of languages, which he knows. This list could be empty, i. e. an employee may know no official languages. But the employees are willing to learn any number of official languages, as long as the company pays their lessons. A study course in one language for one employee costs 1 berdollar.

Find the minimum sum of money the company needs to spend so as any employee could correspond to any other one (their correspondence can be indirect, i. e. other employees can help out translating).

Input

The first line contains two integers n and m (2 ≤ n, m ≤ 100) — the number of employees and the number of languages.

Then n lines follow — each employee's language list. At the beginning of the i-th line is integer ki (0 ≤ ki ≤ m) — the number of languages the i-th employee knows. Next, the i-th line contains ki integers — aij (1 ≤ aij ≤ m) — the identifiers of languages the i-th employee knows. It is guaranteed that all the identifiers in one list are distinct. Note that an employee may know zero languages.

The numbers in the lines are separated by single spaces.

Output

Print a single integer — the minimum amount of money to pay so that in the end every employee could write a letter to every other one (other employees can help out translating).

Examples

Input

5 5
1 2
2 2 3
2 3 4
2 4 5
1 5


Output

0


Input

8 7
0
3 1 2 3
1 1
2 5 4
2 6 7
1 3
2 7 4
1 1


Output

2


Input

2 2
1 2
0


Output

1

Note

In the second sample the employee 1 can learn language 2, and employee 8 can learn language 4.

In the third sample employee 2 must learn language 2.
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.StringTokenizer;

public class C implements CodeforcesSolver {

	public void solve(InputStream is) {
		FastScanner in = new FastScanner(is);
		int n = in.nextInt();
		int m = in.nextInt();

		DisjointSet<String> dset = new DisjointSet<String>();
		for (int i : ZeroTo.get(n))
			dset.makeSet("P" + i);
		for (int i : ZeroTo.get(m))
			dset.makeSet("L" + i);

		boolean allFool = true;
		for (int i : ZeroTo.get(n)) {
			int langCount = in.nextInt();
			if (langCount > 0)
				allFool = false;
			for (int j : ZeroTo.get(langCount)) {
				int lang = in.nextInt() - 1;
				dset.union("P" + i, "L" + lang);
			}
		}

		MutableSet<String> groups = GoodMutableSetFactory.getInstance().create();
		for (int i : ZeroTo.get(n))
			groups.insert(dset.find("P" + i));

		System.out.println(allFool ? n : groups.size() - 1);
	}

	public static void main(String[] args) throws Exception {
		CodeforcesSolverLauncher.launch(new CodeforcesSolverFactory() {
			public CodeforcesSolver create() {
				return new C();
			}
		}, "C1.txt", "C2.txt", "C3.txt");
	}

}

interface CodeforcesSolver {
	void solve(InputStream is); 
}

interface CodeforcesSolverFactory {
	CodeforcesSolver create();
}

class CodeforcesSolverLauncher {

	public static void launch(CodeforcesSolverFactory factory, String... inputFilePath) throws FileNotFoundException, IOException {
		if(System.getProperty("ONLINE_JUDGE", "false").equals("true")) {
			factory.create().solve(System.in);
		} else { 
			for(String path : inputFilePath) {
				FileInputStream is = new FileInputStream(new File(path));
				factory.create().solve(is);
				is.close();
				System.out.println();
			}
		}
	}

}

class FastScanner {
	
	private StringTokenizer tokenizer;

	public FastScanner(InputStream is) {
		try {
			ByteArrayOutputStream bos = new ByteArrayOutputStream(1024*1024);
			byte[] buf = new byte[1024*1024];
			while(true) {
				int read = is.read(buf);
				if(read == -1)
					break;
				bos.write(buf, 0, read);
			}
			tokenizer = new StringTokenizer(new String(bos.toByteArray()));
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	public int nextInt() {
		return Integer.parseInt(tokenizer.nextToken());
	}
	
	public long nextLong() {
		return Long.parseLong(tokenizer.nextToken());
	}
	
	public double nextDouble() {
		return Double.parseDouble(tokenizer.nextToken());
	}
	
	public String next() {
		return tokenizer.nextToken();
	}

}

class GoodMutableSetFactory {
	public static MutableSetFactory getInstance() {
		return JavaHashSetFactory.getInstance();
	}
}

class JavaHashSetFactory {

	public static MutableSetFactory getInstance() {
		return new MutableSetFactory() {
			public <T> MutableSet<T> create() {
				return new MutableSetUsingJavaSet<T>(new HashSet<T>());
			}
		};
	}

}

interface MutableSet<T> extends Set<T> {
	void clear();
	void insert(T v);
	void remove(T v);
}

interface Set<T> extends Collection<T> {
	boolean contains(T v);
}

interface Collection<T> extends Iterable<T> {
	int size();
	boolean isEmpty();
}

interface MutableSetFactory {
	<T> MutableSet<T> create();
}

class MutableSetUsingJavaSet<T> implements MutableSet<T> {
	
	private final java.util.Set<T> javaset;
	
	public MutableSetUsingJavaSet(java.util.Set<T> set) {
		this.javaset = set;
	}

	public void clear() {
		javaset.clear();		
	}
	
	public boolean contains(T v) {
		return javaset.contains(v);		
	}
	
	public void insert(T v) {
		javaset.add(v);
	}

	public Iterator<T> iterator() {
		return javaset.iterator();
	}
	
	public int size() {
		return javaset.size();
	}
	
	public void remove(T v) {
		javaset.remove(v);
	}
	
	public boolean isEmpty() {
		return size() == 0;
	}
	
	public String toString() {
		return IterableToString.toString(this);
	}

}

class IterableToString {
	public static <T> String toString(Iterable<T> iterable) {
		StringBuilder sb = new StringBuilder();
		sb.append('(');
		boolean first = true;
		for(T v : iterable) {
			if(first)
				first = false;
			else
				sb.append(',');
			sb.append(v);
		}
		sb.append(')');
		return sb.toString();
	}
}

class ZeroTo {

	public static Iterable<Integer> get(int end) {
		return IntSequenceIterable.create(0, 1, end);
	}
	
}

class IntSequenceIterable {
	public static Iterable<Integer> create(final int from, final int step, final int size) {
		return new Iterable<Integer>() {
			public Iterator<Integer> iterator() {
				return new ReadOnlyIterator<Integer>() {
					int nextIndex = 0;
					public boolean hasNext() {
						return nextIndex < size;
					}
					public Integer next() {
						return from + step * (nextIndex++);
					}
				};
			}
		};
	}

	private IntSequenceIterable() {
	}
}

abstract class ReadOnlyIterator<T> implements Iterator<T> {
	public final void remove() {
		throw new UnsupportedOperationException();
	}
}

class DisjointSet<T> {
	
	// implementation is the disjoint set forests

	private class NodeInfo {
		T parent;
		int rank;
		public NodeInfo(T p, int rank) {
			this.parent = p;
			this.rank = rank;
		}
	}
	
	private final MutableMap<T, NodeInfo> infoMap = GoodMutableMapFactory.getInstance().create();
	
	public void makeSet(T value) {
		infoMap.put(value, new NodeInfo(value, 0));
	}
	
	public void union(T x, T y) {
		T xrep = find(x);
		T yrep = find(y);
		if (xrep == yrep)
			return;
		NodeInfo xinfo = infoMap.get(xrep);
		NodeInfo yinfo = infoMap.get(yrep);
		if(xinfo.rank > yinfo.rank) {
			yinfo.parent = xrep;
		} else {
			xinfo.parent = yrep;
			if(xinfo.rank == yinfo.rank)
				yinfo.rank ++;
		}
	}
	
	public T find(T x) {
		NodeInfo info = infoMap.get(x);
		if(!info.parent.equals(x))
			info.parent = find(info.parent);
		return info.parent;
	}
	
}

class GoodMutableMapFactory {

	public static MutableMapFactory getInstance() {
		return JavaHashMapFactory.getInstance();
	}

}

class JavaHashMapFactory {

	public static MutableMapFactory getInstance() {
		return new MutableMapFactory() {
			public <K, V> MutableMap<K, V> create() {
				return MutableMapUsingJavaMap.create(new HashMap<K, V>());
			}
		};
	}

	private JavaHashMapFactory() {
	}
}

interface MutableMap<K, V> extends Collection<MutableEntry<K, V>> {
	// TODO introduce normal map interface
	void clear(); 
	void put(K key, V value);
	void remove(K key);
	boolean containsKey(K key);
	V get(K key);
	V get(K key, V def);
	Iterable<V> values();
	Iterable<K> keys();
}

interface MutableEntry<K, V> {
	// TODO introduce normal Entry interface.
	K getKey();
	V getValue();
	void setValue(V v);
}

interface MutableMapFactory {
	<K,V> MutableMap<K,V> create();
}

class MutableMapUsingJavaMap {
	
	public static <K, V> MutableMap<K, V> create(final java.util.Map<K, V> map) {
		return new MutableMap<K, V>() {

			public boolean containsKey(K key) {
				return map.containsKey(key);
			}

			public V get(K key) {
				V r = map.get(key);
				if (r == null)
					throw new RuntimeException();
				return r;
			}

			public V get(K key, V def) {
				V r = map.get(key);
				if (r != null)
					return r;
				else
					return def;
			}

			public Iterable<K> keys() {
				return map.keySet();
			}

			public void put(K key, V value) {
				map.put(key, value);
			}

			public Iterable<V> values() {
				return map.values();
			}

			public void clear() {
				map.clear();
			}

			public boolean isEmpty() {
				return map.isEmpty();
			}

			public Iterator<MutableEntry<K, V>> iterator() {
				return ConvertedDataIterator.create(map.entrySet().iterator(), new DataConverter<java.util.Map.Entry<K, V>, MutableEntry<K, V>>() {
					public MutableEntry<K, V> convert(java.util.Map.Entry<K, V> e) {
						return new EntryWrapper<K, V>(e);
					}
				});
			}

			public int size() {
				return map.size();
			}

			public void remove(K key) {
				map.remove(key);
			}

			public String toString() {
				return map.toString();
			}

		};
	}

	private static class EntryWrapper<K, V> implements MutableEntry<K, V>, EqualityTester<EntryWrapper<K,V>> {
		private java.util.Map.Entry<K, V> e;
		private EntryWrapper(java.util.Map.Entry<K, V> e) {
			this.e = e;
		}
		public K getKey() {
			return e.getKey();
		}
		public V getValue() { 
			return e.getValue();
		}
		public void setValue(V v) {
			e.setValue(v);						
		}
		public boolean equals(Object obj) {
			return StrictEqualityTester.areEqual(this, obj, this);
		}
		public boolean areEqual(EntryWrapper<K, V> o1, EntryWrapper<K, V> o2) {
			return o1.getKey().equals(o2.getKey()) && o1.getValue().equals(o2.getValue());
		}
		public int hashCode() {
			return PairHash.hash(getKey().hashCode(), getValue().hashCode());
		}
	}

}

class ConvertedDataIterator {

	public static <T1, T2> Iterator<T2> create(final Iterator<T1> original, final DataConverter<T1, T2> converter) {
		return new ReadOnlyIterator<T2>() {
			public boolean hasNext() {
				return original.hasNext();
			}
			public T2 next() {
				return converter.convert(original.next());
			}
		};
	}

	private ConvertedDataIterator() {
	}
}

interface DataConverter<T1, T2> {
	T2 convert(T1 v);
}

interface EqualityTester<T> {
	boolean areEqual(T o1, T o2);
}

class StrictEqualityTester {
	@SuppressWarnings("unchecked")
	public static <T> boolean areEqual(T me, Object you, EqualityTester<T> tester) {
		if(me == you)
			return true;
		if(you == null)
			return false;
		if(you.getClass() != me.getClass())
			return false;
		return tester.areEqual(me, (T)you);
	}
}

class PairHash {
	public static int hash(int h1, int h2) {
		return ThomasWangHash.hash64bit((((long) h1) << 32) | h2);
	}
}

class ThomasWangHash {
	
	// from http://www.concentric.net/~ttwang/tech/inthash.htm
	
	public static int hash32bit(int key) {
		key = ~key + (key << 15); // key = (key << 15) - key - 1;
		key = key ^ (key >>> 12);
		key = key + (key << 2);
		key = key ^ (key >>> 4);
		key = key * 2057; // key = (key + (key << 3)) + (key << 11);
		key = key ^ (key >>> 16);
		return key;
	}

	public static int hash64bit(long key) { 
		key = (~key) + (key << 18); // key = (key << 18) - key - 1;
		key = key ^ (key >>> 31);
		key = key * 21; // key = (key + (key << 2)) + (key << 4);
		key = key ^ (key >>> 11);
		key = key + (key << 6);
		key = key ^ (key >>> 22);
		return (int) key;
	}
}
