Problem:
Problem H: Loss

Problem

あなたは、とある会社からプログラムの作成を依頼された。
その会社には$N$個の仕事があり、それぞれの仕事には$1$から$N$までの番号が振られている。
仕事$i$には、$M_i$個の前提となる仕事$X_{i,j}$が存在し、仕事$i$を前提となる仕事$X_{i,j}$よりも先に行うと、仕事$i$は多大な損失を被る。

そこであなたには、損失を被る仕事の数が最小となるような順番ですべての仕事を行ったときの損失を被った仕事の数を求めてもらいたい。

Input

入力は以下の形式ですべて整数で与えられる。

$N$
$M_1$ $X_{1,1}$ $X_{1,2}$ ... $X_{1,M_1}$
$M_2$ $X_{2,1}$ $X_{2,2}$ ... $X_{2,M_2}$
:
$M_N$ $X_{N,1}$ $X_{N,2}$ ...$X_{N,M_N}$

仕事の数$N$が$1$行に与えられる。

続く$N$行に前提となる仕事の情報が与えられる。$i+1$行目には仕事$i$の情報が空白区切りで与えられる。$M_i$は、仕事$i$の前提となる仕事の数、$X_{i,j}$は仕事$i$の前提となる仕事の番号を表している。

Constraints

入力は以下の条件を満たす。

$1 \le N \le 10^5$

$0 \le M_i \le N$

$M_i$の総和は$10^5$を超えない

$1 \le X_{i,j} \le min(i+1,N)$

Output

損失を被る仕事の数が最小になるような順番ですべての仕事を行ったとき、そのときの損失を被った仕事の数を一行に出力する。

Sample Input 1

2
1 1
0

Sample Output 1

1

仕事$1$のように前提となる仕事がその仕事自身となるような入力も存在する。

Sample Input 2

3
3 2 1 2
0
1 2

Sample Output 2

1

Sample Input 3

5
1 2
1 3
2 1 2
3 1 3 5
0

Sample Output 3

1

例えば、$3,2,1,5,4$の順に仕事を行うと損失を最小化できる。

Solution in C++14 (judge verdict: Accepted)

#include <bits/stdc++.h>
using namespace std;

using lint = long long int;

class UnionFind {
private:
	vector< int > data;
	
public:
	UnionFind() {}
	UnionFind(int n) : data(n, -1) {}
	
	bool unionSet(int x, int y) {
		x = root(x); y = root(y);
		
		if (x == y) return 0;
		
		if (data[y] < data[x]) swap(x, y);
		data[x] += data[y]; data[y] = x;
		return 1;
	}
	
	bool sameSet(int x, int y) { return root(x) == root(y); }
	int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
	int size(int x) { return -data[root(x)]; }
};

int N;
vector< set< int > > X;
UnionFind uf;

void solve() {
	vector< int > max_start(N, -1);
	for (int i=0; i<N; ++i) for (int x : X[i]) if (uf.sameSet(x, i)) max_start[i] = max(max_start[i], x);
	
	/*
	for (int i=0; i<N; ++i) cerr << max_start[i] << " ";
	cerr << endl;
	*/
	
	int ans = 0, cur = -1;
	for (int i=0; i<N; ++i) {
		if (max_start[i] >= 0 && cur < max_start[i]) {
			cur = i;
			++ans;
		}
	}
	cout << ans << endl;
}

int main() {
	cin >> N;
	
	X.assign(N, set< int >());	
	uf = UnionFind(N);
	
	for (int i=0; i<N; ++i) {
		int M;
		cin >> M;
		
		for (int j=0; j<M; ++j) {
			int x;
			cin >> x;
			--x;
			if (x == i+1) uf.unionSet(i,i+1);
			else X[i].insert(x);
		}
	}
	
	solve();
}