#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
using namespace std;
const int N = 5005;
int t, n, m;
vector<int> g[N];
stack<int> S;
int pre[N], dfn[N], sccn, sccno[N], dfs_clock;//sccn and sccno[N]表示DAG新图中的点个数,原图点与变换后点之间的映射关系
void dfs(int u) {//Tarjan算法
pre[u] = dfn[u] = ++dfs_clock;
S.push(u);
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if (!pre[v]) {
dfs(v);
dfn[u] = min(dfn[u], dfn[v]);
} else if (!sccno[v]) dfn[u] = min(dfn[u], pre[v]);
}
if (dfn[u] == pre[u]) {
sccn++;
while (1) {
int x = S.top(); S.pop();
sccno[x] = sccn;
if (x == u) break;
}
}
}
void find_scc() {
sccn = dfs_clock = 0;
memset(pre, 0, sizeof(pre));
memset(sccno, 0, sizeof(sccno));
for (int i = 1; i <= n; i++)
if (!pre[i]) dfs(i);//全部遍历
}
int left[N], vis[N];
vector<int> scc[N];//新图
bool match(int u) {//即先前的dfs
for (int i = 0; i < scc[u].size(); i++) {
int v = scc[u][i];
if (vis[v]) continue;
vis[v] = 1;
if (!left[v] || match(left[v])) {
left[v] = u;
return true;
}
}
return false;
}
int hungary() {
memset(left, 0, sizeof(left));
int ans = 0;
for (int i = 1; i <= sccn; i++) {
memset(vis, 0, sizeof(vis));
if (match(i)) ans++;
}
return sccn - ans;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)g[i].clear();
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
g[a].push_back(b);
}
find_scc();
for(int i=1;i<=sccn;i++)scc[i].clear();
for(int i=1;i<=n;i++){
for(int j=0;j<g[i].size();j++){
if(sccno[i]==sccno[g[i][j]])continue;
scc[sccno[i]].push_back(sccno[g[i][j]]);
}
}
printf("%d\n",hungary());
}
return 0;
}
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
using namespace std;
const int N = 5005;
int t, n, m;
vector<int> g[N];
stack<int> S;
int pre[N], dfn[N], sccn, sccno[N], dfs_clock;//sccn and sccno[N]表示DAG新图中的点个数,原图点与变换后点之间的映射关系
void dfs(int u) {//Tarjan算法
pre[u] = dfn[u] = ++dfs_clock;
S.push(u);
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if (!pre[v]) {
dfs(v);
dfn[u] = min(dfn[u], dfn[v]);
} else if (!sccno[v]) dfn[u] = min(dfn[u], pre[v]);
}
if (dfn[u] == pre[u]) {
sccn++;
while (1) {
int x = S.top(); S.pop();
sccno[x] = sccn;
if (x == u) break;
}
}
}
void find_scc() {
sccn = dfs_clock = 0;
memset(pre, 0, sizeof(pre));
memset(sccno, 0, sizeof(sccno));
for (int i = 1; i <= n; i++)
if (!pre[i]) dfs(i);//全部遍历
}
int left[N], vis[N];
vector<int> scc[N];//新图
bool match(int u) {//即先前的dfs
for (int i = 0; i < scc[u].size(); i++) {
int v = scc[u][i];
if (vis[v]) continue;
vis[v] = 1;
if (!left[v] || match(left[v])) {
left[v] = u;
return true;
}
}
return false;
}
int hungary() {
memset(left, 0, sizeof(left));
int ans = 0;
for (int i = 1; i <= sccn; i++) {
memset(vis, 0, sizeof(vis));
if (match(i)) ans++;
}
return sccn - ans;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)g[i].clear();
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
g[a].push_back(b);
}
find_scc();
for(int i=1;i<=sccn;i++)scc[i].clear();
for(int i=1;i<=n;i++){
for(int j=0;j<g[i].size();j++){
if(sccno[i]==sccno[g[i][j]])continue;
scc[sccno[i]].push_back(sccno[g[i][j]]);
}
}
printf("%d\n",hungary());
}
return 0;
}